anna-app-core 0.2.0__tar.gz

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.
@@ -0,0 +1,170 @@
1
+ # matrix agent repository
2
+ matrix/
3
+
4
+ # git diff files
5
+ git.diff
6
+
7
+ # Byte-compiled / optimized / DLL files
8
+ __pycache__/
9
+ *.py[cod]
10
+ *$py.class
11
+
12
+ # C extensions
13
+ *.so
14
+
15
+ # Distribution / packaging
16
+ .Python
17
+ build/
18
+ develop-eggs/
19
+ dist/
20
+ # @anna-ai/app-runtime ships a hand-curated dist/ as the publishable
21
+ # artifact (it is not a build output) — keep it tracked.
22
+ !packages/anna-app-runtime/dist/
23
+ downloads/
24
+ eggs/
25
+ .eggs/
26
+ lib/
27
+ lib64/
28
+ parts/
29
+ sdist/
30
+ var/
31
+ wheels/
32
+ share/python-wheels/
33
+ *.egg-info/
34
+ .installed.cfg
35
+ *.egg
36
+ # setuptools sdist MANIFEST (only at repo root — must NOT swallow the
37
+ # packages/anna-app-schema/manifest/ schema bundle directory).
38
+ /MANIFEST
39
+
40
+ # PyInstaller
41
+ # Anchored to repo root for the same reason — `*.manifest` is too broad
42
+ # and was matching schema bundle files. Re-add specific paths if a real
43
+ # PyInstaller .manifest ever ships.
44
+ /*.manifest
45
+ *.spec
46
+
47
+ # Installer logs
48
+ pip-log.txt
49
+ pip-delete-this-directory.txt
50
+
51
+ # Unit test / coverage reports
52
+ htmlcov/
53
+ .tox/
54
+ .nox/
55
+ .coverage
56
+ .coverage.*
57
+ .cache
58
+ nosetests.xml
59
+ coverage.xml
60
+ *.cover
61
+ *.py,cover
62
+ .hypothesis/
63
+ .pytest_cache/
64
+ cover/
65
+
66
+ # Translations
67
+ *.mo
68
+ *.pot
69
+
70
+ # Django stuff:
71
+ *.log
72
+ local_settings.py
73
+ db.sqlite3
74
+ db.sqlite3-journal
75
+
76
+ # Flask stuff:
77
+ instance/
78
+ .webassets-cache
79
+
80
+ # Scrapy stuff:
81
+ .scrapy
82
+
83
+ # Sphinx documentation
84
+ docs/_build/
85
+
86
+ # PyBuilder
87
+ .pybuilder/
88
+ target/
89
+
90
+ # Jupyter Notebook
91
+ .ipynb_checkpoints
92
+
93
+ # IPython
94
+ profile_default/
95
+ ipython_config.py
96
+
97
+ # pyenv
98
+ .python-version
99
+
100
+ # pipenv
101
+ Pipfile.lock
102
+
103
+ # poetry
104
+ poetry.lock
105
+
106
+ # pdm
107
+ .pdm.toml
108
+
109
+ # PEP 582
110
+ __pypackages__/
111
+
112
+ # Celery stuff
113
+ celerybeat-schedule
114
+ celerybeat.pid
115
+
116
+ # SageMath parsed files
117
+ *.sage.py
118
+
119
+ # Environments
120
+ .env
121
+ .venv
122
+ env/
123
+ venv/
124
+ ENV/
125
+ env.bak/
126
+ venv.bak/
127
+
128
+ # Spyder project settings
129
+ .spyderproject
130
+ .spyproject
131
+
132
+ # Rope project settings
133
+ .ropeproject
134
+
135
+ # mkdocs documentation
136
+ /site
137
+
138
+ # mypy
139
+ .mypy_cache/
140
+ .dmypy.json
141
+ dmypy.json
142
+
143
+ # Pyre type checker
144
+ .pyre/
145
+
146
+ # pytype static type analyzer
147
+ .pytype/
148
+
149
+ # Cython debug symbols
150
+ cython_debug/
151
+
152
+ # IDE
153
+ .idea/
154
+ *.swp
155
+ *.swo
156
+ *~
157
+ .DS_Store
158
+
159
+ # Project specific
160
+ *.tmp
161
+ *.bak
162
+
163
+ # Security - RSA keys
164
+ private_key.pem
165
+ *.pem
166
+ !public_key.pem
167
+
168
+ scripts/nats_auth
169
+ # Anna App runtime cache
170
+ static/anna-apps-cache/
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: anna-app-core
3
+ Version: 0.2.0
4
+ Summary: Anna App platform core: manifest schemas, RPC dispatcher, protocols, errors. Shared by matrix-nexus and the local-dev runtime.
5
+ Project-URL: Documentation, https://github.com/talentai/matrix-nexus/blob/main/docs/design/anna-app-local-dev-and-test.md
6
+ Author: Talent AI
7
+ License: MIT
8
+ Keywords: anna,anna-app,executa
9
+ Requires-Python: >=3.10
10
+ Requires-Dist: pydantic<3.0,>=2.0
11
+ Description-Content-Type: text/markdown
12
+
13
+ # anna-app-core
14
+
15
+ Shared protocol/error/ACL primitives **and** the Anna App RPC dispatcher,
16
+ manifest schemas, and runtime helpers used by the [Anna App](https://github.com/talentai/matrix-nexus/blob/main/docs/design/anna-app-local-dev-and-test.md)
17
+ platform.
18
+
19
+ This package is the **stable contract surface** that both `matrix-nexus`
20
+ (production host) and `anna-app-runtime-local` (local dev harness) depend on,
21
+ so neither has to pull in the other's runtime.
22
+
23
+ ## What's here (v0.2.x)
24
+
25
+ - **Manifest (Pydantic v2)** — `AppManifest`, `UiManifestSection`,
26
+ `UiBundleSection`, `UiViewSpec`, `UiHostApiSpec`, `UiSize`, `WindowViewMeta`,
27
+ `AppDevConfig`, `ManifestExecutaRef`
28
+ - **Dispatcher** — `dispatch(method, params, *, manifest, store, ...)` (the
29
+ full JSON-RPC entry point; previously lived in `matrix-nexus`)
30
+ - **Protocols** — `WindowStoreProtocol` (minimum surface RPC handlers expect
31
+ from a window store)
32
+ - **Errors** — `HostRpcError`, `WindowError`, `WindowPermissionError`
33
+ - **ACL** — `host_api_allows(manifest_dict, ns, method) -> bool`
34
+ - **Runtime helpers** — `select_view`, `scopes_from_manifest`, `view_meta`
35
+ - **Versions** — `DISPATCHER_VERSION`, `SDK_VERSION`, `__version__`
36
+
37
+ ## Versioning
38
+
39
+ `0.2.0a2` — docs catch-up over `0.2.0a1` (no code changes); the dispatcher
40
+ + Pydantic manifest extraction landed in `0.2.0a1`. Pin policy lives in the
41
+ central [VERSIONS.md](../VERSIONS.md) and the operator runbook at
42
+ [`docs/operations/anna-app-release-flow.md`](https://github.com/talentai/matrix-nexus/blob/main/docs/operations/anna-app-release-flow.md).
@@ -0,0 +1,30 @@
1
+ # anna-app-core
2
+
3
+ Shared protocol/error/ACL primitives **and** the Anna App RPC dispatcher,
4
+ manifest schemas, and runtime helpers used by the [Anna App](https://github.com/talentai/matrix-nexus/blob/main/docs/design/anna-app-local-dev-and-test.md)
5
+ platform.
6
+
7
+ This package is the **stable contract surface** that both `matrix-nexus`
8
+ (production host) and `anna-app-runtime-local` (local dev harness) depend on,
9
+ so neither has to pull in the other's runtime.
10
+
11
+ ## What's here (v0.2.x)
12
+
13
+ - **Manifest (Pydantic v2)** — `AppManifest`, `UiManifestSection`,
14
+ `UiBundleSection`, `UiViewSpec`, `UiHostApiSpec`, `UiSize`, `WindowViewMeta`,
15
+ `AppDevConfig`, `ManifestExecutaRef`
16
+ - **Dispatcher** — `dispatch(method, params, *, manifest, store, ...)` (the
17
+ full JSON-RPC entry point; previously lived in `matrix-nexus`)
18
+ - **Protocols** — `WindowStoreProtocol` (minimum surface RPC handlers expect
19
+ from a window store)
20
+ - **Errors** — `HostRpcError`, `WindowError`, `WindowPermissionError`
21
+ - **ACL** — `host_api_allows(manifest_dict, ns, method) -> bool`
22
+ - **Runtime helpers** — `select_view`, `scopes_from_manifest`, `view_meta`
23
+ - **Versions** — `DISPATCHER_VERSION`, `SDK_VERSION`, `__version__`
24
+
25
+ ## Versioning
26
+
27
+ `0.2.0a2` — docs catch-up over `0.2.0a1` (no code changes); the dispatcher
28
+ + Pydantic manifest extraction landed in `0.2.0a1`. Pin policy lives in the
29
+ central [VERSIONS.md](../VERSIONS.md) and the operator runbook at
30
+ [`docs/operations/anna-app-release-flow.md`](https://github.com/talentai/matrix-nexus/blob/main/docs/operations/anna-app-release-flow.md).
@@ -0,0 +1,25 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "anna-app-core"
7
+ version = "0.2.0"
8
+ description = "Anna App platform core: manifest schemas, RPC dispatcher, protocols, errors. Shared by matrix-nexus and the local-dev runtime."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Talent AI" }]
13
+ keywords = ["anna", "anna-app", "executa"]
14
+
15
+ # pydantic v2 is required for the AppManifest schema; everything else is
16
+ # pure stdlib.
17
+ dependencies = [
18
+ "pydantic>=2.0,<3.0",
19
+ ]
20
+
21
+ [project.urls]
22
+ Documentation = "https://github.com/talentai/matrix-nexus/blob/main/docs/design/anna-app-local-dev-and-test.md"
23
+
24
+ [tool.hatch.build.targets.wheel]
25
+ packages = ["src/anna_app_core"]
@@ -0,0 +1,60 @@
1
+ """anna-app-core: stable contract surface shared by matrix-nexus and
2
+ anna-app-runtime-local.
3
+
4
+ v0.2.0a1 — full extraction. The complete RPC dispatcher, manifest pydantic
5
+ schemas, and helper functions now live here; matrix-nexus re-exports them
6
+ for backward compatibility (see README.md).
7
+
8
+ v0.2.0a2 — docs catch-up only (README rewrite). No API changes.
9
+ """
10
+
11
+ from .acl import host_api_allows
12
+ from .dispatcher import dispatch
13
+ from .errors import HostRpcError, WindowError, WindowPermissionError
14
+ from .manifest import (
15
+ AppDevConfig,
16
+ AppManifest,
17
+ ManifestExecutaRef,
18
+ UiBundleSection,
19
+ UiHostApiSpec,
20
+ UiManifestSection,
21
+ UiSize,
22
+ UiViewSpec,
23
+ WindowViewMeta,
24
+ )
25
+ from .protocols import WindowStoreProtocol
26
+ from .runtime import scopes_from_manifest, select_view, view_meta
27
+ from .versions import DISPATCHER_VERSION, SDK_VERSION
28
+
29
+ __version__ = "0.2.0"
30
+
31
+ __all__ = [
32
+ # errors
33
+ "HostRpcError",
34
+ "WindowError",
35
+ "WindowPermissionError",
36
+ # protocols
37
+ "WindowStoreProtocol",
38
+ # acl
39
+ "host_api_allows",
40
+ # dispatcher
41
+ "dispatch",
42
+ # manifest schemas
43
+ "AppManifest",
44
+ "AppDevConfig",
45
+ "ManifestExecutaRef",
46
+ "UiBundleSection",
47
+ "UiHostApiSpec",
48
+ "UiManifestSection",
49
+ "UiSize",
50
+ "UiViewSpec",
51
+ "WindowViewMeta",
52
+ # runtime helpers
53
+ "select_view",
54
+ "scopes_from_manifest",
55
+ "view_meta",
56
+ # versions
57
+ "DISPATCHER_VERSION",
58
+ "SDK_VERSION",
59
+ "__version__",
60
+ ]
@@ -0,0 +1,151 @@
1
+ """Host-API ACL check.
2
+
3
+ Accepts either a typed ``AppManifest`` (preferred — production + harness)
4
+ or a plain ``dict`` (e.g. JSON-loaded manifest before pydantic validation).
5
+
6
+ Mirrors the pre-extraction implementation in
7
+ ``matrix-nexus/src/services/anna_app_runtime_service.py::host_api_allows``.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Any, Union
13
+
14
+ from .manifest import AppManifest
15
+
16
+
17
+ def _ui_host_api(
18
+ manifest: Union[AppManifest, dict[str, Any]],
19
+ ) -> dict[str, Any] | None:
20
+ """Return the ``ui.host_api`` mapping (ns -> list[method] | dict) or
21
+ ``None`` if the manifest has no UI section.
22
+
23
+ Note: most namespaces resolve to ``list[str]`` of allowed methods, but
24
+ ``agent`` is a structured dict (see ``AgentHostApiSpec``).
25
+ """
26
+ if isinstance(manifest, AppManifest):
27
+ if manifest.ui is None:
28
+ return None
29
+ api = manifest.ui.host_api
30
+ return {
31
+ "tools": api.tools,
32
+ "chat": api.chat,
33
+ "artifact": api.artifact,
34
+ "llm": api.llm,
35
+ "agent": api.agent.model_dump() if api.agent is not None else None,
36
+ "fs": api.fs,
37
+ "storage": api.storage,
38
+ "prefs": api.prefs,
39
+ "files": api.files,
40
+ "user_files": api.user_files,
41
+ }
42
+ ui = manifest.get("ui") if isinstance(manifest, dict) else None
43
+ if ui is None:
44
+ return None
45
+ return ui.get("host_api") or {}
46
+
47
+
48
+ def host_api_allows(
49
+ manifest: Union[AppManifest, dict[str, Any]],
50
+ ns: str,
51
+ method: str,
52
+ ) -> bool:
53
+ """Return ``True`` iff ``ns.method`` is granted by the manifest's ACL.
54
+
55
+ `window.*` is always granted. ``tools.invoke`` and ``tools.list`` are
56
+ granted whenever the ``tools`` set is non-empty (per-tool gating happens
57
+ inside the handler). ``agent.session.*`` is granted when at least one
58
+ submode (``auto`` / ``fixed``) is enabled — the handler validates the
59
+ selected submode against the structured spec.
60
+ """
61
+ if ns == "window":
62
+ return True
63
+ api_map = _ui_host_api(manifest)
64
+ if api_map is None:
65
+ return False
66
+ methods = api_map.get(ns)
67
+ if not methods:
68
+ return False
69
+ if ns == "agent":
70
+ # Structured form: ``{session: {auto: bool, fixed: {client_ids:[]}}, tools: [...]}``.
71
+ if not method.startswith("session."):
72
+ return False
73
+ sess = methods.get("session") if isinstance(methods, dict) else None
74
+ if not isinstance(sess, dict):
75
+ return False
76
+ auto_on = bool(sess.get("auto"))
77
+ fixed_on = bool(sess.get("fixed"))
78
+ return auto_on or fixed_on
79
+ if isinstance(methods, list):
80
+ if "*" in methods:
81
+ return True
82
+ if method in methods:
83
+ return True
84
+ if ns == "tools" and method in ("invoke", "list"):
85
+ return True
86
+ return False
87
+
88
+
89
+ # -----------------------------------------------------------------------------
90
+ # Scope capability — manifest.host_capabilities
91
+ # -----------------------------------------------------------------------------
92
+
93
+
94
+ _ACTIONS = ("read", "write")
95
+ _SCOPES = ("user", "app", "tool")
96
+
97
+
98
+ def _host_caps_list(
99
+ manifest: Union[AppManifest, dict[str, Any]],
100
+ ) -> list[str]:
101
+ """Normalise ``host_capabilities`` to a flat list[str]. Tolerates dict form."""
102
+ if isinstance(manifest, AppManifest):
103
+ raw: Any = list(manifest.host_capabilities or [])
104
+ else:
105
+ raw = manifest.get("host_capabilities") if isinstance(manifest, dict) else None
106
+ raw = raw or []
107
+ if isinstance(raw, dict):
108
+ return [str(k) for k, v in raw.items() if v]
109
+ if isinstance(raw, (list, tuple)):
110
+ return [str(c) for c in raw]
111
+ return []
112
+
113
+
114
+ def host_capability_allows_scope(
115
+ manifest: Union[AppManifest, dict[str, Any]],
116
+ scope: str,
117
+ action: str,
118
+ *,
119
+ cross_owner: bool = False,
120
+ ) -> bool:
121
+ """Return ``True`` iff the manifest grants APS access to ``(scope, action)``.
122
+
123
+ ``scope`` is one of ``user`` / ``app`` / ``tool``; ``action`` is ``read`` or
124
+ ``write``. ``cross_owner`` is ``True`` when the call targets a different
125
+ ``owner_id`` than the calling iframe (e.g. another app's ``scope=app``
126
+ bucket). Self-owned ``scope=app`` access stays granted by the legacy
127
+ ``aps.kv`` capability.
128
+
129
+ Capability strings recognised:
130
+
131
+ * ``aps.scope.admin`` → grants everything.
132
+ * ``aps.scope.<scope>.<action>`` → exact match.
133
+ * ``aps.kv`` → ``scope=app`` self-owned only.
134
+ """
135
+ if scope not in _SCOPES:
136
+ return False
137
+ if action not in _ACTIONS:
138
+ return False
139
+ caps = set(_host_caps_list(manifest))
140
+ if "aps.scope.admin" in caps:
141
+ return True
142
+ fine = f"aps.scope.{scope}.{action}"
143
+ if fine in caps:
144
+ return True
145
+ # Legacy: ``aps.kv`` covers self-owned scope=app reads & writes.
146
+ if scope == "app" and not cross_owner and ("aps.kv" in caps or "aps" in caps):
147
+ return True
148
+ return False
149
+
150
+
151
+ __all__ = ["host_api_allows", "host_capability_allows_scope"]