ltcai 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/docs/CHANGELOG.md +50 -0
- package/docs/EDITION_STRATEGY.md +56 -0
- package/docs/ENTERPRISE.md +78 -0
- package/latticeai/__init__.py +1 -1
- package/latticeai/core/enterprise.py +152 -0
- package/latticeai/core/workspace_os.py +409 -38
- package/latticeai/server_app.py +188 -8
- package/package.json +1 -1
- package/static/scripts/workspace.js +149 -0
- package/static/sw.js +1 -1
- package/static/workspace.css +31 -0
- package/static/workspace.html +41 -2
package/README.md
CHANGED
|
@@ -36,6 +36,21 @@ Automatic knowledge graph
|
|
|
36
36
|
Graph-aware chat, snapshots, memory, agents, workflows, skills, and timeline
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
### New in 1.1.0: Organization Workspace Foundation
|
|
40
|
+
|
|
41
|
+
- **Organization Workspace** alongside Personal Workspace — create shared org
|
|
42
|
+
workspaces, list/switch between them, and archive (non-destructively)
|
|
43
|
+
- **Workspace roles & permissions** — `owner`, `admin`, `member`, `viewer`
|
|
44
|
+
mapped to read / write / manage-members / manage-workspace
|
|
45
|
+
- **Workspace-scoped data** — snapshots, memory, agent runs, workflows, traces,
|
|
46
|
+
and timeline carry a `workspace_id`; reads scope via the `X-Workspace-Id` header
|
|
47
|
+
- **CI / release hardening** — Node.js 24 ready workflow, version-scoped
|
|
48
|
+
artifact upload (never `dist/*`), and a release artifact validator
|
|
49
|
+
- **Enterprise extension foundation (open-core)** — a stable seam for a future
|
|
50
|
+
Enterprise plugin; Community ships everything it has today, unrestricted
|
|
51
|
+
(see [docs/ENTERPRISE.md](docs/ENTERPRISE.md) and
|
|
52
|
+
[docs/EDITION_STRATEGY.md](docs/EDITION_STRATEGY.md))
|
|
53
|
+
|
|
39
54
|
### New in 1.0.0: AI Workspace OS
|
|
40
55
|
|
|
41
56
|
- Workspace OS command center at `/workspace`
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.1.0] - 2026-05-31
|
|
4
|
+
|
|
5
|
+
> Organization Workspace foundation, open-core Enterprise seam, and CI/release hardening.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Organization Workspace foundation** — Workspace OS now distinguishes
|
|
10
|
+
`personal` and `organization` workspace types. A full workspace model
|
|
11
|
+
(`workspace_id`, `name`, `type`, `owner_user_id`, `members`, `roles`,
|
|
12
|
+
`settings`, `created_at`, `updated_at`, `status`) is stored in the existing
|
|
13
|
+
local-first JSON store.
|
|
14
|
+
- **Organization Workspace API** — create org workspace, list workspaces, get
|
|
15
|
+
workspace, update, archive (soft, non-destructive), add/remove member, update
|
|
16
|
+
member role, get workspace summary, activate workspace, and an edition info
|
|
17
|
+
endpoint, exposed under `/workspace/orgs/*`, `/workspace/registry`,
|
|
18
|
+
`/workspace/activate`, and `/workspace/editions`.
|
|
19
|
+
- **Workspace roles and permissions** — `owner`, `admin`, `member`, `viewer`
|
|
20
|
+
mapped to `read` / `write` / `manage_members` / `manage_workspace`. Owners and
|
|
21
|
+
admins manage settings and members; members use the workspace; viewers are
|
|
22
|
+
read-only. Personal workspaces always grant their local user owner rights.
|
|
23
|
+
- **Workspace-scoped data** — Snapshots, Memories, Agent runs, Workflows, answer
|
|
24
|
+
Traces, and Timeline events now carry a `workspace_id`. Reads accept an
|
|
25
|
+
optional `X-Workspace-Id` header / `workspace_id` query to scope results.
|
|
26
|
+
- **Enterprise extension seam (open-core)** — new `latticeai/core/enterprise.py`
|
|
27
|
+
defines an `Edition` enum (`community`/`enterprise`), an
|
|
28
|
+
`EnterpriseCapability` enum, and a runtime `CapabilityRegistry` that a future,
|
|
29
|
+
separately-distributed Enterprise plugin can attach a provider to. The
|
|
30
|
+
Community build ships **zero** enabled Enterprise capabilities and restricts no
|
|
31
|
+
Community feature. Documented in `docs/ENTERPRISE.md` and
|
|
32
|
+
`docs/EDITION_STRATEGY.md`.
|
|
33
|
+
- **Release artifact validator** — `scripts/validate_release_artifacts.py`
|
|
34
|
+
verifies that exactly the expected `whl`/`tar.gz`/`vsix`/`tgz` exist for a
|
|
35
|
+
single version, that internal versions match, that the VSIX contains
|
|
36
|
+
`extension/out/extension.js`, and warns when `dist/` mixes other versions.
|
|
37
|
+
- **Workspace OS UI** — Personal/Organization workspace switcher, current
|
|
38
|
+
workspace indicator, and a minimal organization create / member / role panel
|
|
39
|
+
wired into the existing Workspace OS command center.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- **CI / release hardening** — `release.yml` opts into Node.js 24
|
|
44
|
+
(`FORCE_JAVASCRIPT_ACTIONS_TO_NODE24`) and bumps `actions/checkout@v5`,
|
|
45
|
+
`actions/setup-node@v5`, `actions/setup-python@v6`. Artifact upload and
|
|
46
|
+
`twine check` are now scoped to the tagged version only — never a `dist/*`
|
|
47
|
+
glob — and the build runs the release artifact validator before upload.
|
|
48
|
+
- Existing 1.0.x Workspace OS state is migrated non-destructively to the v1.1
|
|
49
|
+
workspace model on load; legacy records map to the Personal workspace.
|
|
50
|
+
- Release metadata aligned to `1.1.0` across Python, npm, VS Code extension,
|
|
51
|
+
FastAPI app metadata, and `/health`.
|
|
52
|
+
|
|
3
53
|
## [1.0.1] - 2026-05-31
|
|
4
54
|
|
|
5
55
|
> CI packaging fix for the VS Code extension build.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Lattice AI — Edition Strategy (Open Core)
|
|
2
|
+
|
|
3
|
+
This document records the principles behind the Community / Enterprise split so
|
|
4
|
+
the boundary stays predictable for contributors and users.
|
|
5
|
+
|
|
6
|
+
## Editions
|
|
7
|
+
|
|
8
|
+
- **Community** — this repository, MIT licensed. Local-first AI Workspace OS:
|
|
9
|
+
local LLMs, knowledge graph, Personal **and** Organization workspaces,
|
|
10
|
+
role-based membership, snapshots, memory, agents, workflows, skills, and the
|
|
11
|
+
auditable timeline. Community is a complete product.
|
|
12
|
+
- **Enterprise** — a separately-distributed plugin adding organization-scale
|
|
13
|
+
governance, identity, compliance, and deployment capabilities. Distributed and
|
|
14
|
+
licensed separately. See [ENTERPRISE.md](ENTERPRISE.md).
|
|
15
|
+
|
|
16
|
+
## Principles
|
|
17
|
+
|
|
18
|
+
1. **Community is never crippled.** No existing Community feature is removed,
|
|
19
|
+
throttled, or gated to push Enterprise. The capability seam only answers "is
|
|
20
|
+
this *Enterprise* capability available?" — `False` in the open build — and
|
|
21
|
+
never disables a Community code path.
|
|
22
|
+
2. **Open-core boundary is a runtime seam, not a fork.** Enterprise attaches via
|
|
23
|
+
`capability_registry.register_provider(...)`
|
|
24
|
+
(`latticeai/core/enterprise.py`). The Community repository contains no
|
|
25
|
+
Enterprise implementation and imports no Enterprise code.
|
|
26
|
+
3. **Capabilities are declared, not hidden.** `EnterpriseCapability` enumerates
|
|
27
|
+
reserved capabilities so the contract is visible and stable, even though
|
|
28
|
+
Community implements none of them.
|
|
29
|
+
4. **Local-first stays the default.** Enterprise adds options (tenant isolation,
|
|
30
|
+
air-gapped deployment, SIEM export); it does not move the default experience
|
|
31
|
+
off the user's machine.
|
|
32
|
+
5. **Graceful by default.** A misbehaving or absent provider must never break a
|
|
33
|
+
Community request; the registry falls back to the Community provider.
|
|
34
|
+
|
|
35
|
+
## What lives where
|
|
36
|
+
|
|
37
|
+
| Concern | Community (this repo) | Enterprise (separate) |
|
|
38
|
+
|--------|------------------------|------------------------|
|
|
39
|
+
| Personal & Organization workspaces | ✅ | — |
|
|
40
|
+
| Base roles (owner/admin/member/viewer) | ✅ | — |
|
|
41
|
+
| Snapshots / memory / agents / workflows / skills | ✅ | — |
|
|
42
|
+
| Audit timeline (local) | ✅ | — |
|
|
43
|
+
| Capability seam & enum | ✅ (declares) | ✅ (implements) |
|
|
44
|
+
| SSO/SCIM/IdP provisioning | seam only | ✅ |
|
|
45
|
+
| Tenant isolation, compliance retention, eDiscovery | seam only | ✅ |
|
|
46
|
+
| SIEM export, DLP, admin policy packs | seam only | ✅ |
|
|
47
|
+
| Private VPC / air-gapped deployment | seam only | ✅ |
|
|
48
|
+
|
|
49
|
+
## Detecting edition at runtime
|
|
50
|
+
|
|
51
|
+
- `GET /workspace/editions` → edition + per-capability matrix.
|
|
52
|
+
- `GET /workspace/os` → `edition` block in the Workspace OS summary.
|
|
53
|
+
- `latticeai.core.enterprise.detect_edition()` for in-process checks.
|
|
54
|
+
|
|
55
|
+
In the Community build all of the above report `community` with every Enterprise
|
|
56
|
+
capability `false`.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Lattice AI — Enterprise Edition
|
|
2
|
+
|
|
3
|
+
> **Status:** Foundation / extension seam only. The open-source Community
|
|
4
|
+
> edition in this repository ships **none** of the capabilities below enabled,
|
|
5
|
+
> and contains **no** Enterprise implementation. This document describes the
|
|
6
|
+
> boundary and the roadmap, not shipped code.
|
|
7
|
+
|
|
8
|
+
Lattice AI follows an **open-core** model:
|
|
9
|
+
|
|
10
|
+
- **Community** (this repository, MIT) is fully functional on its own: local
|
|
11
|
+
LLMs, knowledge graph, Personal and Organization workspaces, roles, snapshots,
|
|
12
|
+
memory, agents, workflows, skills, and the auditable timeline.
|
|
13
|
+
- **Enterprise** is a separately-distributed plugin that attaches advanced,
|
|
14
|
+
organization-scale governance and deployment capabilities through a stable
|
|
15
|
+
runtime seam. It is never bundled into the Community build.
|
|
16
|
+
|
|
17
|
+
See [EDITION_STRATEGY.md](EDITION_STRATEGY.md) for the principles that keep this
|
|
18
|
+
boundary honest (Community is never crippled to upsell Enterprise).
|
|
19
|
+
|
|
20
|
+
## The extension seam
|
|
21
|
+
|
|
22
|
+
The seam lives in [`latticeai/core/enterprise.py`](../latticeai/core/enterprise.py):
|
|
23
|
+
|
|
24
|
+
- `Edition` — `community` (default) or `enterprise`.
|
|
25
|
+
- `EnterpriseCapability` — the enum of reserved capabilities (below).
|
|
26
|
+
- `CapabilityProvider` — the protocol an Enterprise plugin implements.
|
|
27
|
+
- `capability_registry` — a process-wide registry. An Enterprise plugin calls
|
|
28
|
+
`capability_registry.register_provider(...)` at startup. Until then, the
|
|
29
|
+
default `CommunityCapabilityProvider` answers every capability query with
|
|
30
|
+
"Community / disabled".
|
|
31
|
+
|
|
32
|
+
Community code consults the seam at extension points via
|
|
33
|
+
`is_capability_enabled(EnterpriseCapability.X)`. In the Community build this is
|
|
34
|
+
always `False`, so the Community code path is taken and nothing is gated off.
|
|
35
|
+
|
|
36
|
+
The live edition + capability matrix is exposed at `GET /workspace/editions`
|
|
37
|
+
and surfaced in the Workspace OS summary (`GET /workspace/os` → `edition`).
|
|
38
|
+
|
|
39
|
+
## Enterprise capability roadmap
|
|
40
|
+
|
|
41
|
+
These are declared in `EnterpriseCapability` so the seam is stable and
|
|
42
|
+
discoverable. None are implemented in Community.
|
|
43
|
+
|
|
44
|
+
| Capability | Enum | Summary |
|
|
45
|
+
|-----------|------|---------|
|
|
46
|
+
| Advanced SSO | `SSO_ADVANCED` | Enforced SSO, session policy, SAML/OIDC federation |
|
|
47
|
+
| IdP provisioning | `IDP_PROVISIONING` | Entra ID / Okta user & group provisioning |
|
|
48
|
+
| SCIM | `SCIM` | SCIM 2.0 user/group lifecycle sync |
|
|
49
|
+
| Advanced RBAC/ABAC | `RBAC_ABAC_ADVANCED` | Custom roles, attribute-based policy beyond the 4 base roles |
|
|
50
|
+
| Tenant isolation | `TENANT_ISOLATION` | Hard multi-tenant data isolation |
|
|
51
|
+
| Compliance retention | `COMPLIANCE_RETENTION` | Legal-hold, retention windows, immutable audit |
|
|
52
|
+
| SIEM export | `SIEM_EXPORT` | Streaming audit/event export to Splunk/Sentinel/etc. |
|
|
53
|
+
| Private VPC | `PRIVATE_VPC` | Private-network / customer-VPC deployment controls |
|
|
54
|
+
| Air-gapped deployment | `AIR_GAPPED_DEPLOYMENT` | Fully offline install & update channel |
|
|
55
|
+
| DLP policy | `DLP_POLICY` | Data-loss-prevention scanning & enforcement |
|
|
56
|
+
| eDiscovery | `EDISCOVERY` | Search, hold, and export for legal discovery |
|
|
57
|
+
| Admin policy packs | `ADMIN_POLICY_PACKS` | Centrally-managed org policy bundles |
|
|
58
|
+
|
|
59
|
+
## How an Enterprise plugin attaches (illustrative)
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from latticeai.core.enterprise import (
|
|
63
|
+
Edition, EnterpriseCapability, capability_registry,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
class EnterpriseProvider:
|
|
67
|
+
def edition(self) -> Edition:
|
|
68
|
+
return Edition.ENTERPRISE
|
|
69
|
+
|
|
70
|
+
def is_enabled(self, capability: EnterpriseCapability) -> bool:
|
|
71
|
+
return capability in MY_LICENSED_CAPABILITIES
|
|
72
|
+
|
|
73
|
+
# Enterprise package startup:
|
|
74
|
+
capability_registry.register_provider(EnterpriseProvider())
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
No Enterprise code or credentials live in this repository; the Community build
|
|
78
|
+
imports none of the above.
|
package/latticeai/__init__.py
CHANGED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Enterprise extension seam (open-core boundary).
|
|
2
|
+
|
|
3
|
+
LatticeAI Community is fully functional on its own. This module defines the
|
|
4
|
+
*seam* where a future, separately-distributed Enterprise plugin can attach
|
|
5
|
+
advanced capabilities (SSO provisioning, SCIM, tenant isolation, compliance
|
|
6
|
+
retention, SIEM export, and so on) **without** any of that code living in the
|
|
7
|
+
public Community repository.
|
|
8
|
+
|
|
9
|
+
Design rules enforced here:
|
|
10
|
+
|
|
11
|
+
* Community is the default :class:`Edition`. Every :class:`EnterpriseCapability`
|
|
12
|
+
is **disabled** unless an Enterprise provider is explicitly registered.
|
|
13
|
+
* Nothing in this module restricts or gates a Community feature. It only answers
|
|
14
|
+
"is this *Enterprise* capability available?" — which is ``False`` in the open
|
|
15
|
+
build.
|
|
16
|
+
* Enterprise behaviour is supplied at runtime through
|
|
17
|
+
:class:`CapabilityProvider` implementations registered on the shared
|
|
18
|
+
:data:`capability_registry`. The public code never imports Enterprise code.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import os
|
|
24
|
+
from enum import Enum
|
|
25
|
+
from typing import Dict, List, Optional, Protocol, runtime_checkable
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Edition(str, Enum):
|
|
29
|
+
"""Distribution edition. Community is the open-source default."""
|
|
30
|
+
|
|
31
|
+
COMMUNITY = "community"
|
|
32
|
+
ENTERPRISE = "enterprise"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class EnterpriseCapability(str, Enum):
|
|
36
|
+
"""Capabilities reserved for the Enterprise edition.
|
|
37
|
+
|
|
38
|
+
These are documented in ``docs/ENTERPRISE.md``. They are intentionally
|
|
39
|
+
declared (so the seam is stable and discoverable) but never implemented in
|
|
40
|
+
the Community build.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
SSO_ADVANCED = "sso_advanced"
|
|
44
|
+
IDP_PROVISIONING = "idp_provisioning" # Entra ID / Okta provisioning
|
|
45
|
+
SCIM = "scim"
|
|
46
|
+
RBAC_ABAC_ADVANCED = "rbac_abac_advanced"
|
|
47
|
+
TENANT_ISOLATION = "tenant_isolation"
|
|
48
|
+
COMPLIANCE_RETENTION = "compliance_retention"
|
|
49
|
+
SIEM_EXPORT = "siem_export"
|
|
50
|
+
PRIVATE_VPC = "private_vpc"
|
|
51
|
+
AIR_GAPPED_DEPLOYMENT = "air_gapped_deployment"
|
|
52
|
+
DLP_POLICY = "dlp_policy"
|
|
53
|
+
EDISCOVERY = "ediscovery"
|
|
54
|
+
ADMIN_POLICY_PACKS = "admin_policy_packs"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@runtime_checkable
|
|
58
|
+
class CapabilityProvider(Protocol):
|
|
59
|
+
"""Contract an Enterprise plugin implements to light up capabilities.
|
|
60
|
+
|
|
61
|
+
A provider is registered at runtime via
|
|
62
|
+
:meth:`CapabilityRegistry.register_provider`. The Community build ships no
|
|
63
|
+
provider, so :meth:`is_enabled` is effectively ``False`` everywhere.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def edition(self) -> Edition:
|
|
67
|
+
...
|
|
68
|
+
|
|
69
|
+
def is_enabled(self, capability: EnterpriseCapability) -> bool:
|
|
70
|
+
...
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class CommunityCapabilityProvider:
|
|
74
|
+
"""Default provider: Community edition, no Enterprise capabilities."""
|
|
75
|
+
|
|
76
|
+
def edition(self) -> Edition:
|
|
77
|
+
return Edition.COMMUNITY
|
|
78
|
+
|
|
79
|
+
def is_enabled(self, capability: EnterpriseCapability) -> bool: # noqa: ARG002
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class CapabilityRegistry:
|
|
84
|
+
"""Runtime seam that an Enterprise plugin can attach a provider to.
|
|
85
|
+
|
|
86
|
+
The registry holds at most one active provider. Until an Enterprise build
|
|
87
|
+
registers one, the :class:`CommunityCapabilityProvider` answers every query
|
|
88
|
+
with "Community / disabled".
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
def __init__(self) -> None:
|
|
92
|
+
self._provider: CapabilityProvider = CommunityCapabilityProvider()
|
|
93
|
+
|
|
94
|
+
def register_provider(self, provider: CapabilityProvider) -> None:
|
|
95
|
+
if not isinstance(provider, CapabilityProvider):
|
|
96
|
+
raise TypeError("provider must implement the CapabilityProvider protocol")
|
|
97
|
+
self._provider = provider
|
|
98
|
+
|
|
99
|
+
def reset(self) -> None:
|
|
100
|
+
"""Restore the Community default provider (used by tests)."""
|
|
101
|
+
self._provider = CommunityCapabilityProvider()
|
|
102
|
+
|
|
103
|
+
def edition(self) -> Edition:
|
|
104
|
+
return self._provider.edition()
|
|
105
|
+
|
|
106
|
+
def is_capability_enabled(self, capability: EnterpriseCapability) -> bool:
|
|
107
|
+
try:
|
|
108
|
+
return bool(self._provider.is_enabled(capability))
|
|
109
|
+
except Exception:
|
|
110
|
+
# A misbehaving plugin must never break a Community request.
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
def available_capabilities(self) -> List[EnterpriseCapability]:
|
|
114
|
+
return [cap for cap in EnterpriseCapability if self.is_capability_enabled(cap)]
|
|
115
|
+
|
|
116
|
+
def describe(self) -> Dict[str, object]:
|
|
117
|
+
"""Edition + capability matrix for ``/workspace/editions`` and admin UI."""
|
|
118
|
+
return {
|
|
119
|
+
"edition": self.edition().value,
|
|
120
|
+
"is_enterprise": self.edition() is Edition.ENTERPRISE,
|
|
121
|
+
"capabilities": {
|
|
122
|
+
cap.value: self.is_capability_enabled(cap) for cap in EnterpriseCapability
|
|
123
|
+
},
|
|
124
|
+
"community_notice": (
|
|
125
|
+
"All listed capabilities are Enterprise-only extension points. "
|
|
126
|
+
"The open-source Community edition ships none of them enabled; "
|
|
127
|
+
"see docs/ENTERPRISE.md and docs/EDITION_STRATEGY.md."
|
|
128
|
+
),
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# Shared singleton seam. Enterprise plugins import this and call
|
|
133
|
+
# ``capability_registry.register_provider(...)`` during their own startup.
|
|
134
|
+
capability_registry = CapabilityRegistry()
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def detect_edition() -> Edition:
|
|
138
|
+
"""Best-effort edition detection.
|
|
139
|
+
|
|
140
|
+
Honours an explicit ``LATTICE_EDITION=enterprise`` opt-in for environments
|
|
141
|
+
that have separately installed an Enterprise provider, but otherwise the
|
|
142
|
+
registry's active provider is the source of truth.
|
|
143
|
+
"""
|
|
144
|
+
env = os.environ.get("LATTICE_EDITION", "").strip().lower()
|
|
145
|
+
if env == Edition.ENTERPRISE.value and capability_registry.edition() is Edition.ENTERPRISE:
|
|
146
|
+
return Edition.ENTERPRISE
|
|
147
|
+
return capability_registry.edition()
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def is_capability_enabled(capability: EnterpriseCapability) -> bool:
|
|
151
|
+
"""Module-level convenience used by Community code at extension points."""
|
|
152
|
+
return capability_registry.is_capability_enabled(capability)
|