iagent-mesh 0.4.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.
Files changed (65) hide show
  1. iagent_mesh-0.4.0/.gitignore +10 -0
  2. iagent_mesh-0.4.0/LICENSE +21 -0
  3. iagent_mesh-0.4.0/PKG-INFO +84 -0
  4. iagent_mesh-0.4.0/README.md +45 -0
  5. iagent_mesh-0.4.0/app.py +99 -0
  6. iagent_mesh-0.4.0/docs/HANDOFF-meshtool-execute.md +238 -0
  7. iagent_mesh-0.4.0/docs/architecture_manifesto.md +162 -0
  8. iagent_mesh-0.4.0/docs/jupyter_guide.md +255 -0
  9. iagent_mesh-0.4.0/docs/outbound/README.md +56 -0
  10. iagent_mesh-0.4.0/docs/outbound/sdk-blocking-sync-handlers.patch +81 -0
  11. iagent_mesh-0.4.0/docs/outbound/sdk-discards-caller-identity.patch +107 -0
  12. iagent_mesh-0.4.0/iagent_mesh/__init__.py +27 -0
  13. iagent_mesh-0.4.0/iagent_mesh/client.py +189 -0
  14. iagent_mesh-0.4.0/iagent_mesh/config.py +62 -0
  15. iagent_mesh-0.4.0/iagent_mesh/core.py +618 -0
  16. iagent_mesh-0.4.0/iagent_mesh/identity_stanzas.py +77 -0
  17. iagent_mesh-0.4.0/iagent_mesh/models.py +16 -0
  18. iagent_mesh-0.4.0/iagent_mesh/registration_transport.py +145 -0
  19. iagent_mesh-0.4.0/iagent_mesh/scaffold_core.py +150 -0
  20. iagent_mesh-0.4.0/iagent_mesh/service_identity.py +95 -0
  21. iagent_mesh-0.4.0/iagent_mesh/shapes.py +163 -0
  22. iagent_mesh-0.4.0/iagent_mesh/transport_auth.py +492 -0
  23. iagent_mesh-0.4.0/mcp_server/__init__.py +1 -0
  24. iagent_mesh-0.4.0/mcp_server/server.py +71 -0
  25. iagent_mesh-0.4.0/pyproject.toml +89 -0
  26. iagent_mesh-0.4.0/pytest.ini +2 -0
  27. iagent_mesh-0.4.0/templates/01_pure_math/app.py +42 -0
  28. iagent_mesh-0.4.0/templates/01_pure_math/pyproject.toml +9 -0
  29. iagent_mesh-0.4.0/templates/01_pure_math/template.yaml +2 -0
  30. iagent_mesh-0.4.0/templates/02_instructor_polars/app.py +61 -0
  31. iagent_mesh-0.4.0/templates/02_instructor_polars/prompts/instructions.yaml +2 -0
  32. iagent_mesh-0.4.0/templates/02_instructor_polars/pyproject.toml +13 -0
  33. iagent_mesh-0.4.0/templates/02_instructor_polars/template.yaml +2 -0
  34. iagent_mesh-0.4.0/templates/03_baml_pandas/app.py +49 -0
  35. iagent_mesh-0.4.0/templates/03_baml_pandas/baml_src/schema.baml +11 -0
  36. iagent_mesh-0.4.0/templates/03_baml_pandas/pyproject.toml +12 -0
  37. iagent_mesh-0.4.0/templates/03_baml_pandas/template.yaml +2 -0
  38. iagent_mesh-0.4.0/templates/legacy_adapter/app.py +70 -0
  39. iagent_mesh-0.4.0/templates/legacy_adapter/pyproject.toml +15 -0
  40. iagent_mesh-0.4.0/templates/smolagents_subswarm/app.py +60 -0
  41. iagent_mesh-0.4.0/templates/smolagents_subswarm/pyproject.toml +14 -0
  42. iagent_mesh-0.4.0/tests/__init__.py +1 -0
  43. iagent_mesh-0.4.0/tests/conftest.py +22 -0
  44. iagent_mesh-0.4.0/tests/test_app.py +117 -0
  45. iagent_mesh-0.4.0/tests/test_caller_identity_reaches_handler.py +239 -0
  46. iagent_mesh-0.4.0/tests/test_client.py +169 -0
  47. iagent_mesh-0.4.0/tests/test_consumer_contracts_invincible_agent.py +118 -0
  48. iagent_mesh-0.4.0/tests/test_core.py +500 -0
  49. iagent_mesh-0.4.0/tests/test_cortex_data_client_contract.py +113 -0
  50. iagent_mesh-0.4.0/tests/test_disclosure_policy.py +177 -0
  51. iagent_mesh-0.4.0/tests/test_docs_disabled.py +75 -0
  52. iagent_mesh-0.4.0/tests/test_gauge_visibility.py +229 -0
  53. iagent_mesh-0.4.0/tests/test_mcp_server.py +156 -0
  54. iagent_mesh-0.4.0/tests/test_models.py +61 -0
  55. iagent_mesh-0.4.0/tests/test_packaging.py +119 -0
  56. iagent_mesh-0.4.0/tests/test_per_user_read_end_to_end.py +164 -0
  57. iagent_mesh-0.4.0/tests/test_postponed_annotations.py +110 -0
  58. iagent_mesh-0.4.0/tests/test_probe_path_exemption.py +146 -0
  59. iagent_mesh-0.4.0/tests/test_registration_consumer_is_bound.py +117 -0
  60. iagent_mesh-0.4.0/tests/test_registration_transport.py +116 -0
  61. iagent_mesh-0.4.0/tests/test_scaffold_core.py +121 -0
  62. iagent_mesh-0.4.0/tests/test_scoping_posture_is_declared.py +181 -0
  63. iagent_mesh-0.4.0/tests/test_sync_handlers_do_not_block.py +195 -0
  64. iagent_mesh-0.4.0/tests/test_templates.py +97 -0
  65. iagent_mesh-0.4.0/tests/test_transport_auth.py +171 -0
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .env
4
+ .venv
5
+ venv/
6
+ .pytest_cache/
7
+ .coverage
8
+ build/
9
+ dist/
10
+ *.egg-info/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Edgy Solutions
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software χωρίς restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.5
2
+ Name: iagent_mesh
3
+ Version: 0.4.0
4
+ Summary: Universal contracts and infrastructure wrapper for the iagent Mesh — typed predicate registration, transport auth, and per-caller identity for mesh tools
5
+ Project-URL: Homepage, https://github.com/edgy-solutions/iagent-mesh-sdk
6
+ Project-URL: Repository, https://github.com/edgy-solutions/iagent-mesh-sdk
7
+ Project-URL: Issues, https://github.com/edgy-solutions/iagent-mesh-sdk/issues
8
+ Author-email: Chris Nogradi <cnogradi@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: agents,ai-mesh,datahub,fastapi,knowledge-graph,predicate-graph,topaz
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Framework :: FastAPI
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Libraries
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: acryl-datahub>=0.13.0
24
+ Requires-Dist: fastapi>=0.100.0
25
+ Requires-Dist: httpx>=0.24.0
26
+ Requires-Dist: mcp<2,>=1.0.0
27
+ Requires-Dist: nest-asyncio>=1.5.0
28
+ Requires-Dist: pydantic-settings>=2.0.0
29
+ Requires-Dist: pydantic>=2.0.0
30
+ Requires-Dist: pyjwt>=2.0.0
31
+ Requires-Dist: pyyaml>=6.0
32
+ Requires-Dist: uvicorn>=0.23.0
33
+ Provides-Extra: dev
34
+ Requires-Dist: httpx; extra == 'dev'
35
+ Requires-Dist: pytest; extra == 'dev'
36
+ Requires-Dist: pytest-asyncio; extra == 'dev'
37
+ Requires-Dist: pytest-mock; extra == 'dev'
38
+ Description-Content-Type: text/markdown
39
+
40
+ # iagent-mesh-sdk (DevEx Hub)
41
+
42
+ This repository serves as the Data-Driven Developer Experience (DevEx) Hub and provides the `iagent_mesh` SDK core library, which includes universal contracts and an infrastructure wrapper for the iagent Mesh platform.
43
+
44
+ > 📖 **Architectural Guide:** Read [Launch Domain Nodes, Not Scripts](docs/architecture_manifesto.md) to understand why this framework is required for enterprise AI deployments.
45
+
46
+ ## Architecture & Features
47
+
48
+ This Hub leverages the **Inception Pattern** to dynamically scaffold new agent tools.
49
+
50
+ - **`iagent_mesh` Core Library**: Contains `MeshTool` for routing, FastAPI execution, and Topaz Zero-Trust security.
51
+ - **Templates**: Standardized data scientist environments (Pure Math, Instructor + Polars, BAML + Pandas).
52
+ - **Interactive Scaffolding**: Use `scripts/scaffold.sh` to locally generate a new agent from a template.
53
+ - **MCP Server**: The `mcp_server/server.py` exposes tools (`scaffold_local_workspace`, `publish_local_to_mesh`) for intelligent IDE-based agent creation.
54
+ - **Cloud Endpoints**: `app.py` exposes REST APIs (`scaffold_generator`, `mesh_publisher`) for the central orchestrator to provision workspaces dynamically.
55
+
56
+ ## Environment Configuration
57
+
58
+ This SDK relies on centralized environment variables for all integrations to ensure no hardcoded enterprise strings are checked in. See the `.env.example` file in the root directory for a full list.
59
+
60
+ **Required URLs:**
61
+ - `GIT_PROVISION_API_URL`: The platform provisioning API for repositories.
62
+ - `GIT_SERVER_HOST`: The core Git server hostname.
63
+ - `ARTIFACTORY_BASE_URL`: The enterprise artifact repository.
64
+
65
+ **Optional URLs / tokens (only needed if `MESH_REGISTER_ON_STARTUP=true`):**
66
+ - `DATAHUB_GMS_URL`: The DataHub GMS endpoint the SDK pushes registrations to.
67
+ - `DATAHUB_TOKEN`: Bearer token for the GMS emitter.
68
+
69
+ **Required Tokens (Depending on deployment):**
70
+ - `PLATFORM_GIT_TOKEN`: Used by the cloud pod (`app.py`) for live git publishing.
71
+ - `MESH_DEV_TOKEN`: Used by the local `mcp_server` to authenticate against provisioning APIs.
72
+
73
+ ## Installation & Testing
74
+
75
+ Data scientists can install the core SDK via:
76
+ ```bash
77
+ uv pip install git+https://[your-repo-url]
78
+ ```
79
+
80
+ To run the robust regression testing suite:
81
+ ```bash
82
+ uv pip install -e ".[dev]"
83
+ python -m pytest
84
+ ```
@@ -0,0 +1,45 @@
1
+ # iagent-mesh-sdk (DevEx Hub)
2
+
3
+ This repository serves as the Data-Driven Developer Experience (DevEx) Hub and provides the `iagent_mesh` SDK core library, which includes universal contracts and an infrastructure wrapper for the iagent Mesh platform.
4
+
5
+ > 📖 **Architectural Guide:** Read [Launch Domain Nodes, Not Scripts](docs/architecture_manifesto.md) to understand why this framework is required for enterprise AI deployments.
6
+
7
+ ## Architecture & Features
8
+
9
+ This Hub leverages the **Inception Pattern** to dynamically scaffold new agent tools.
10
+
11
+ - **`iagent_mesh` Core Library**: Contains `MeshTool` for routing, FastAPI execution, and Topaz Zero-Trust security.
12
+ - **Templates**: Standardized data scientist environments (Pure Math, Instructor + Polars, BAML + Pandas).
13
+ - **Interactive Scaffolding**: Use `scripts/scaffold.sh` to locally generate a new agent from a template.
14
+ - **MCP Server**: The `mcp_server/server.py` exposes tools (`scaffold_local_workspace`, `publish_local_to_mesh`) for intelligent IDE-based agent creation.
15
+ - **Cloud Endpoints**: `app.py` exposes REST APIs (`scaffold_generator`, `mesh_publisher`) for the central orchestrator to provision workspaces dynamically.
16
+
17
+ ## Environment Configuration
18
+
19
+ This SDK relies on centralized environment variables for all integrations to ensure no hardcoded enterprise strings are checked in. See the `.env.example` file in the root directory for a full list.
20
+
21
+ **Required URLs:**
22
+ - `GIT_PROVISION_API_URL`: The platform provisioning API for repositories.
23
+ - `GIT_SERVER_HOST`: The core Git server hostname.
24
+ - `ARTIFACTORY_BASE_URL`: The enterprise artifact repository.
25
+
26
+ **Optional URLs / tokens (only needed if `MESH_REGISTER_ON_STARTUP=true`):**
27
+ - `DATAHUB_GMS_URL`: The DataHub GMS endpoint the SDK pushes registrations to.
28
+ - `DATAHUB_TOKEN`: Bearer token for the GMS emitter.
29
+
30
+ **Required Tokens (Depending on deployment):**
31
+ - `PLATFORM_GIT_TOKEN`: Used by the cloud pod (`app.py`) for live git publishing.
32
+ - `MESH_DEV_TOKEN`: Used by the local `mcp_server` to authenticate against provisioning APIs.
33
+
34
+ ## Installation & Testing
35
+
36
+ Data scientists can install the core SDK via:
37
+ ```bash
38
+ uv pip install git+https://[your-repo-url]
39
+ ```
40
+
41
+ To run the robust regression testing suite:
42
+ ```bash
43
+ uv pip install -e ".[dev]"
44
+ python -m pytest
45
+ ```
@@ -0,0 +1,99 @@
1
+ import os
2
+ import uuid
3
+ import subprocess
4
+ from fastapi import FastAPI
5
+ from pydantic import Field
6
+ from iagent_mesh.models import ToolInput, ToolOutput
7
+ from iagent_mesh.core import MeshTool
8
+ from iagent_mesh.scaffold_core import generate_template_files, publish_workspace_to_git
9
+ from iagent_mesh.config import settings
10
+
11
+ # 1. Define Schemas
12
+ class ScaffoldInput(ToolInput):
13
+ template_id: str = Field(...)
14
+ tool_name: str = Field(...)
15
+ is_mcp: bool = Field(False)
16
+
17
+ class ScaffoldOutput(ToolOutput):
18
+ workspace_uuid: str
19
+ target_path: str
20
+
21
+ class PublishInput(ToolInput):
22
+ workspace_uuid: str = Field(...)
23
+ tool_name: str = Field(...)
24
+ is_mcp: bool = Field(False)
25
+ target_git_group: str = Field(...)
26
+
27
+ class PublishOutput(ToolOutput):
28
+ status: str
29
+ git_url: str
30
+
31
+ # 2. Initialize MeshTools — these are platform tools (the DevEx Hub itself
32
+ # exists only because the mesh exists), so they use the ``mesh:`` namespace
33
+ # per ADR-0005. The predicate edges they create are:
34
+ #
35
+ # (mesh:ScaffoldRequest) --[mesh:scaffoldWorkspace]--> (mesh:ScaffoldedWorkspace)
36
+ # (mesh:PublishRequest) --[mesh:publishWorkspace]--> (mesh:GitDeployment)
37
+ scaffold_tool = MeshTool(
38
+ name="scaffold_generator",
39
+ description="Scaffolds a DevEx template into a fresh workspace directory.",
40
+ verb="mesh:scaffoldWorkspace",
41
+ input_uri="mesh:ScaffoldRequest",
42
+ output_uri="mesh:ScaffoldedWorkspace",
43
+ verb_synonyms=["scaffold", "generate workspace", "create from template"],
44
+ owner_persona="DATA_STEWARD",
45
+ cost_class="fast",
46
+ )
47
+ publish_tool = MeshTool(
48
+ name="mesh_publisher",
49
+ description="Publishes a scaffolded workspace to a managed git remote.",
50
+ verb="mesh:publishWorkspace",
51
+ input_uri="mesh:PublishRequest",
52
+ output_uri="mesh:GitDeployment",
53
+ verb_synonyms=["publish", "push to git", "deploy workspace"],
54
+ owner_persona="DATA_STEWARD",
55
+ cost_class="medium",
56
+ )
57
+
58
+ # 3. Define Tool Logic
59
+ @scaffold_tool.execute()
60
+ def run_scaffold(data: ScaffoldInput) -> ScaffoldOutput:
61
+ workspace_uuid = str(uuid.uuid4())
62
+ target_path = f"/tmp/{workspace_uuid}"
63
+
64
+ # Enforce standardized URN based on type
65
+ if data.is_mcp:
66
+ tool_urn = f"urn:li:mcpServer:{data.tool_name}"
67
+ else:
68
+ tool_urn = f"urn:li:aitool:{data.tool_name}"
69
+
70
+ generate_template_files(data.template_id, data.tool_name, tool_urn, target_path)
71
+
72
+ return ScaffoldOutput(workspace_uuid=workspace_uuid, target_path=target_path)
73
+
74
+ @publish_tool.execute()
75
+ def run_publish(data: PublishInput) -> PublishOutput:
76
+ target_path = f"/tmp/{data.workspace_uuid}"
77
+ if not os.path.exists(target_path):
78
+ raise ValueError(f"Workspace not found at {target_path}")
79
+
80
+ git_token = settings.require("PLATFORM_GIT_TOKEN")
81
+ git_host = settings.require("GIT_SERVER_HOST")
82
+ git_url = f"https://oauth2:{git_token}@{git_host}/{data.target_git_group}/{data.tool_name}.git"
83
+
84
+ # Use centralized utility
85
+ try:
86
+ publish_workspace_to_git(target_path, git_url)
87
+ except RuntimeError as e:
88
+ raise ValueError(str(e))
89
+
90
+ return PublishOutput(status="Success", git_url=git_url)
91
+
92
+ # 4. Master App
93
+ app = FastAPI(title="DevEx Hub Cloud Tools")
94
+ app.mount("/scaffold", scaffold_tool.app)
95
+ app.mount("/publish", publish_tool.app)
96
+
97
+ if __name__ == "__main__":
98
+ import uvicorn
99
+ uvicorn.run(app, host="0.0.0.0", port=8000)
@@ -0,0 +1,238 @@
1
+ > ## ✅ RESOLVED in v0.4.0 — both findings, landed together
2
+ >
3
+ > **Disposition: BOTH, in one change to `route_handler`** — the option this packet argued for.
4
+ > They were not landed separately, so the `run_in_executor` composition hazard described below
5
+ > never had a window to occur.
6
+ >
7
+ > * **Finding A** — a parameter annotated `CallerIdentity` (any name) now receives the invoker;
8
+ > `current_caller()` reads the same identity from a request-scoped `ContextVar` where no
9
+ > parameter can be threaded; `require_authz_id()` is the fail-closed read accessor.
10
+ > * **Finding B** — sync handlers run via `anyio.to_thread.run_sync` under an explicit
11
+ > `contextvars.copy_context()`, so the contextvar survives into the worker thread. The
12
+ > quickstart's promise is now true rather than corrected away.
13
+ > * **The silence** — the packet asked that not consuming the identity be *visible*. An
14
+ > undeclared unscoped handler now WARNS at registration; `@app.execute(caller_scoped=False)`
15
+ > records deliberate intent and silences it. All five shipped templates declare a posture.
16
+ >
17
+ > **Both rulings were answered by enumeration, not preference.** The census the packet demanded
18
+ > found ZERO `MeshTool.execute()` handlers in invincible-agent (zero `MeshTool` call sites at
19
+ > all), so the population holding retroactively-broken sync handlers was empty — fix the code,
20
+ > no migration debt.
21
+ >
22
+ > **Version:** ADDITIVE for tool authors — the single-parameter form is unchanged and untouched
23
+ > handlers keep working. Execution *semantics* change for sync handlers (they now run off the
24
+ > event loop), and undeclared handlers gain a startup warning. The `!` on v0.4.0 is for
25
+ > `MeshClient.ask()` and the `requires-python` bump, not for `execute()`.
26
+ >
27
+ > Seals: `tests/test_caller_identity_reaches_handler.py` (incl. an executable root-cause seal
28
+ > pinning that FastAPI discards app-level dependency return values),
29
+ > `tests/test_per_user_read_end_to_end.py` (three-caller discrimination),
30
+ > `tests/test_sync_handlers_do_not_block.py` (incl. `test_THE_COORDINATION_TEST...`),
31
+ > `tests/test_scoping_posture_is_declared.py`. Each was broken on purpose and shown red first.
32
+ >
33
+ > Original packet preserved below.
34
+
35
+ # Handoff — two defects in `MeshTool.execute()`, and why they are one job
36
+
37
+ > ## ✅ RESOLVED in 0.4.0 (2026-08-27)
38
+ >
39
+ > Both findings fixed together, in one change to `route_handler`, as this document required.
40
+ >
41
+ > * **Finding A** — a parameter annotated `CallerIdentity` receives the invoker;
42
+ > `current_caller()` reads the same identity from a request-scoped `ContextVar` with no
43
+ > parameter. `CallerIdentity.require_authz_id()` is the fail-closed read accessor: it raises
44
+ > rather than returning the `None` that silently becomes a service read.
45
+ > * **Finding B** — sync handlers run via `anyio.to_thread.run_sync` under an explicit
46
+ > `contextvars.copy_context()`, so the doc's threading promise is now true *and* A's contextvar
47
+ > survives into the worker thread.
48
+ >
49
+ > **Both rulings were answered by enumeration, not preference.** The handler census the document
50
+ > demanded returned **zero** `MeshTool.execute()` handlers in invincible-agent (and zero
51
+ > `MeshTool` / `MeshClient` call sites at all) — so the population holding retroactively-broken
52
+ > sync handlers was empty, and fixing the code carried no migration debt. Ruling 1 → fix the
53
+ > code. Ruling 2 → the count is zero; the five in-repo templates all use the unchanged
54
+ > single-parameter form.
55
+ >
56
+ > **The interaction this document predicted was reproduced, not assumed.** Implementing B with
57
+ > `loop.run_in_executor` passes every threading test while `current_caller()` reads `None` inside
58
+ > the recommended handler style. That is why the acceptance criterion below is a test named
59
+ > `test_THE_COORDINATION_TEST_...` — it is the only one that fails in that configuration.
60
+ >
61
+ > Tests: `tests/test_caller_identity_reaches_handler.py`,
62
+ > `tests/test_sync_handlers_do_not_block.py`, `tests/test_per_user_read_end_to_end.py`,
63
+ > `tests/test_cortex_data_client_contract.py`. Each fix was broken on purpose, shown red for its
64
+ > own reason, and restored.
65
+ >
66
+ > **Found while fixing these** (and fixed): `execute()` read the handler's annotation raw, so any
67
+ > tool module using `from __future__ import annotations` 422'd on *every* request with
68
+ > `'str' object is not callable` — see `tests/test_postponed_annotations.py`.
69
+ >
70
+ > Bridge to the data plane is `originator_email=caller.require_authz_id()` — dag-tools'
71
+ > `CortexDataClient` takes a string subject, carried opaque, so employee-id deployments work
72
+ > unchanged. `caller=` was not adopted because that parameter does not exist in dag-tools yet;
73
+ > `test_cortex_data_client_contract.py` watches its real source and goes red when it appears.
74
+
75
+ **To:** whoever owns `iagent-mesh-sdk`
76
+ **From:** the ADR-0044 / mesh-read session, 2026-08-27
77
+ **Both findings are in `iagent_mesh/core.py`, in the same function.**
78
+
79
+ Plan items (invincible-agent `docs/plans/`, on `docs/BOARD.md`):
80
+ `[[sdk-discards-caller-identity]]`, `[[sdk-blocking-sync-handlers]]`.
81
+
82
+ ---
83
+
84
+ ## Read this first: they touch the same seam, so sequence them
85
+
86
+ Both fixes change `MeshTool.execute()`'s `route_handler`. **Two uncoordinated fixes to one
87
+ function is how it grows a third defect**, and in this case they interact directly:
88
+
89
+ - Fix A puts a `CallerIdentity` in a request-scoped `ContextVar`.
90
+ - Fix B moves sync handlers off the event loop onto a thread.
91
+ - **`asyncio.to_thread` copies the context. `loop.run_in_executor` does not.**
92
+
93
+ Do B with `run_in_executor` and A's contextvar reads `None` inside **exactly the handler style
94
+ the quickstart recommends** — and without A's fail-closed rule, that read lands on the service
95
+ identity, silently, in the most common case. Two correct-looking fixes producing a
96
+ cross-tenant read.
97
+
98
+ Do them together, or A then B with B's mechanism chosen for A.
99
+
100
+ ---
101
+
102
+ ## Finding A — a tool cannot learn who invoked it
103
+
104
+ **`core.py:180`** registers the auth dependency at APP level:
105
+
106
+ ```python
107
+ dependencies=[Depends(make_transport_auth_dependency(component=name))],
108
+ ```
109
+
110
+ `make_transport_auth_dependency` does the right thing — it returns a `CallerIdentity` whose
111
+ `authz_id` is documented as *"the ONLY field an authorization decision may key on."* But
112
+ **FastAPI discards an app-level dependency's return value.** It is not injectable into a route,
113
+ never reaches `request.state`, and `route_handler` does not ask for it.
114
+
115
+ Then **`core.py:440`**:
116
+
117
+ ```python
118
+ return func(input_data)
119
+ ```
120
+
121
+ The handler receives the validated input model and nothing else.
122
+
123
+ **So the caller is computed, logged, and thrown away** — one frame before anyone could use it.
124
+
125
+ ### What that forces
126
+
127
+ A tool author has nothing to put here:
128
+
129
+ ```python
130
+ client = CortexDataClient(originator_email=???)
131
+ ```
132
+
133
+ Their only working option is a bare constructor, which resolves to the **service identity**. So
134
+ **every user of that agent reads with the service's entitlements** — the confused deputy the
135
+ platform's own chart comment warns about on `CORTEX_CLIENT_ID`.
136
+
137
+ **It fails invisibly.** An agent reading as the service *works*. Rows come back, nothing errors,
138
+ no test fails. The only symptom is that every user sees data entitled to the service.
139
+
140
+ **Existence proof:** Engine DA does not get the caller from the SDK. It pulls `user_email` off
141
+ the request payload (`agent_fleet/data_analyst/main.py:271`) because the supervisor threads it
142
+ manually. The one agent doing per-user reads correctly had to route around the SDK.
143
+
144
+ ### The target
145
+
146
+ ```python
147
+ @app.execute()
148
+ def detect_anomalies(data: AnomalyInput, caller: CallerIdentity) -> AnomalyOutput:
149
+ client = CortexDataClient(caller=caller)
150
+ ```
151
+
152
+ …and better, with a contextvar, no parameter at all:
153
+
154
+ ```python
155
+ client = CortexDataClient() # notebook, pipeline, agent handler — identical
156
+ ```
157
+
158
+ **The resolution order matters more than the mechanism, and it is the part to get right:**
159
+
160
+ > 1. explicit `caller=` — override, for tests. Wins over everything.
161
+ > 2. the request-scoped contextvar — the agent case.
162
+ > 3. `CORTEX_USER_TOKEN` — per-process user identity (notebook).
163
+ > 4. service identity — **opt-in only.**
164
+ >
165
+ > **Inside a request, failure to resolve RAISES.** It never falls through to 3 or 4.
166
+
167
+ **Why 2 must outrank 3:** everyone writing this by hand reaches for
168
+
169
+ ```python
170
+ return os.environ['CORTEX_USER_TOKEN'] if 'CORTEX_USER_TOKEN' in os.environ else caller
171
+ ```
172
+
173
+ which works in all three contexts today — **only because that var happens to be unset on agent
174
+ pods.** Set it once for debugging and every request silently reads as one user, with code that
175
+ still looks correct. A confused deputy arriving via a *config* change, with nothing to review.
176
+
177
+ Steps 1 and 2 ship together. The contextvar without the fail-closed rule just relocates the
178
+ silent service-read from "author forgot `caller=`" to "contextvar was empty for a reason nobody
179
+ noticed."
180
+
181
+ ---
182
+
183
+ ## Finding B — the quickstart promises a background thread that does not exist
184
+
185
+ **`core.py:438`**:
186
+
187
+ ```python
188
+ if inspect.iscoroutinefunction(func):
189
+ return await func(input_data)
190
+ return func(input_data) # no threadpool
191
+ ```
192
+
193
+ **`docs/jupyter_guide.md`**:
194
+
195
+ > **Use standard `def` (Recommended):** If you are crunching Polars DataFrames
196
+ > (`df.collect()`), stick to standard `def`. **We will execute it safely in a background
197
+ > thread.**
198
+
199
+ `grep -rn "run_in_threadpool\|to_thread\|run_in_executor" iagent_mesh/*.py` → nothing.
200
+
201
+ A recommended handler doing `df.collect()` holds the event loop for its duration. Every other
202
+ request to that tool, including health probes, waits.
203
+
204
+ **The doc recommended the failing path for the heaviest workload**, so authors who complied are
205
+ worse off than authors who ignored it.
206
+
207
+ ### Two rulings, neither of them technical
208
+
209
+ 1. **Fix the code or fix the doc?** Threading makes every existing sync handler retroactively
210
+ correct — and changes execution semantics for handlers written assuming single-threaded
211
+ module state. Correcting the doc makes them retroactively wrong and obliges migration. The
212
+ choice decides **which population currently holds broken code.**
213
+ 2. **Who audits existing handlers, and how many are there?** Neither option can be sized
214
+ without a count. Enumerate before choosing — a remembered list of "the tools we know about"
215
+ is how the sixth one breaks.
216
+
217
+ ---
218
+
219
+ ## Acceptance for the pair
220
+
221
+ - A tool handler can name its invoker; a read inside it authorizes as **that person**.
222
+ - Two different users invoking the same agent against the same asset get **different rows**.
223
+ - `CORTEX_USER_TOKEN` set on an agent pod changes nothing — the request's caller outranks it.
224
+ - Reading as the service requires saying so.
225
+ - A sync handler doing a multi-second `collect()` does not delay a concurrent request.
226
+ - **If threaded: a `ContextVar` set by the auth dependency is readable inside a sync handler.**
227
+ This is the test that proves the two fixes were coordinated.
228
+
229
+ ## Context worth having
230
+
231
+ The mesh's read path was hardened today (invincible-agent ADR-0044, dag-tools 0.3.2): routing
232
+ tickets now carry only broker-minted credentials, scoped to the asset and expiring with the
233
+ access window. That closed *what a caller is handed*. Finding A is *whether an agent can say
234
+ who is asking* — and it is the blocker on the notebook→agent path, which is where the users
235
+ are actually heading.
236
+
237
+ Not filed by an agent unilaterally because both rulings are blast-radius questions, not
238
+ technical preferences.
@@ -0,0 +1,162 @@
1
+ # THE iagent MESH: LAUNCH DOMAIN NODES, NOT SCRIPTS
2
+ ### An Architectural Guide for Data Science and AI Engineering Teams
3
+
4
+ Your team’s primary directive is to engineer state-of-the-art AI agents and custom user experiences. However, the path to production often forces data scientists to become part-time cloud infrastructure engineers. In an enterprise environment, AI cannot operate in the shadows; agents must be assigned to persons, and their actions must be entirely traceable, predictable, observable, grounded, and trustworthy.
5
+
6
+ This document outlines the architectural realities of deploying AI services in our enterprise ecosystem, comparing the traditional Do-It-Yourself (DIY) API deployment against adopting the native `MeshTool` framework.
7
+
8
+ ---
9
+
10
+ ## PART 1: THE DIY REALITY (THE HIDDEN COST OF CUSTOM INFRASTRUCTURE)
11
+
12
+ If you choose to bypass the framework and deploy your agents as standalone APIs (e.g., raw FastAPI/Flask) to serve your custom UIs, you assume full responsibility for the enterprise plumbing.
13
+
14
+ Here is the infrastructure you are now required to build, maintain, and secure to meet enterprise standards:
15
+
16
+ * **Identity & Policy Enforcement:** Enterprise agents must be definitively assigned to real persons. You must manually write middleware to intercept Keycloak JWTs, parse claims, and enforce complex data access policies on every single incoming request to prove authorization.
17
+ * **Data Access & Grounding:** To be trustworthy, an agent must be firmly grounded in secure enterprise data. You cannot simply ask for a dataset; you must manage your own AWS STS token exchanges, handle credential rotation, and manually enforce Row-Level Security (RLS) and column masking to remain compliant.
18
+ * **Distributed Tracing (The Black Hole):** An AI system must be completely observable. When your custom UI calls your agent, and your agent calls a vector DB or an LLM, the trace is easily broken. You must manually inject Langfuse observers, pass trace IDs through your thread pools, and manage the `contextvars` yourself. When a hallucination or failure occurs, your logging is siloed and untraceable.
19
+ * **Concurrency & Predictability:** AI behavior must be predictable. If your agent uses `asyncio` mixed with synchronous data crunching (Pandas/Polars) or nested sub-agents (`smolagents`), you risk freezing your event loop and crashing your Kubernetes pods under load unless you manually patch and manage thread pools.
20
+ * **Deployment Boilerplate:** You own the Dockerfiles, the Kubernetes YAMLs, the Helm charts, and the CI/CD pipeline definitions required to get your code into the cluster.
21
+
22
+ **The Result:** Your team spends 80% of its velocity maintaining backend plumbing and proving compliance, and only 20% engineering the actual AI and the custom UI.
23
+
24
+ ---
25
+
26
+ ## PART 2: THE `MeshTool` ADVANTAGE
27
+
28
+ The `MeshTool` framework is not a restriction; it is an exoskeleton. It provides a hardened, headless backend for your custom UI, absorbing all enterprise infrastructure and compliance complexity into three lines of code.
29
+
30
+ ### 1. Frictionless, Trustworthy Data Plane
31
+ Never manage an AWS key, database password, or access policy again.
32
+ * **Identity Passthrough:** Agents are inherently assigned to persons. The moment you use `CortexDataClient()`, the mesh invisibly routes your user's Keycloak JWT through the cluster, ensuring every action is tied to a verified human.
33
+ * **Native Compliance & Grounding:** The platform automatically enforces central data access policies, RLS, and column masking before the data ever hits your agent. Your agent is guaranteed to be grounded in trustworthy, authorized enterprise data.
34
+ * **High-Throughput Ready:** Seamless integration with `dag_tools` returns Polars LazyFrames for instant, massive-scale data crunching without crashing your LLM context windows.
35
+
36
+ ### 2. Complete Observability Out-of-the-Box
37
+ Debugging a nested AI loop shouldn't require guesswork.
38
+ * **End-to-End Traceability:** The mesh generates a unified Trace ID from your custom UI's HTTP header and propagates it flawlessly across all network boundaries and asynchronous threads.
39
+ * **Langfuse Native:** Every LLM call, tool use, and latency spike inside your agent is automatically tracked, made fully observable, and tied to the exact user session.
40
+
41
+ ### 3. Predictable Concurrency & DevOps
42
+ You write the Python; the platform ensures predictable execution.
43
+ * **Safe Asynchronous Execution:** The framework natively applies `nest_asyncio` and thread-pool routing. You can spin up heavy data operations or nested agent loops without ever freezing your API.
44
+ * **One-Click Deployment:** Use the interactive `scaffold.sh` to generate a GitOps-ready repository. Push your code, and Jenkins automatically builds the container (via S2I) and deploys it. Zero Dockerfiles required.
45
+
46
+ ### 4. Headless Architecture (Bring Your Own UI)
47
+ We do not force you into a centralized UI.
48
+ * **Perfect Portability:** `MeshTool` exposes standard, OpenAPI-compliant endpoints. Your custom React, Vue, or Streamlit frontend can communicate with your agent exactly as it does today.
49
+ * **Global Discoverability:** While you power your custom UI, the framework automatically registers your agent's OpenAPI schema to the DataHub Universal Registry. Engine A (the central mesh) can now discover and route relevant enterprise traffic to your endpoint, giving your domain expertise massive internal distribution for free.
50
+
51
+ ---
52
+
53
+ ## PART 3: SYSTEMS ARCHITECTURE (DoDAF VIEWS)
54
+
55
+ ### The OV-1: Operational Concept Graphic
56
+ This diagram contrasts the high engineering burden of the DIY approach against the streamlined elegance of the Mesh ecosystem.
57
+
58
+ ```mermaid
59
+ %%{init: {'theme': 'neutral'} }%%
60
+ flowchart TD
61
+ classDef nightmare fill:#ffe6e6,stroke:#ff3333,stroke-width:2px,color:#990000;
62
+ classDef mesh fill:#e6f3ff,stroke:#3399ff,stroke-width:2px,color:#004c99;
63
+ classDef platform fill:#e6ffe6,stroke:#33cc33,stroke-width:2px,color:#006600;
64
+
65
+ subgraph DIY[The DIY Silo: High Engineering Burden]
66
+ DS1((Data Scientist)) --> API[Custom API Engine]
67
+ API --> Auth[Manual Token Middleware]:::nightmare
68
+ API --> Trace[Fragmented Local Logging]:::nightmare
69
+ API --> DBConn[Raw AWS/DB Connections]:::nightmare
70
+ Auth -.-> Risk[Compliance Violations]
71
+ end
72
+
73
+ subgraph MESH[The iagent Mesh Ecosystem]
74
+ DS2((Data Scientist)) --> Logic[AI Agent / Business Logic]
75
+ Logic --> MT[MeshTool Node Wrapper]:::mesh
76
+
77
+ Orchestrator[Engine A: Central Orchestrator] == Authenticated Traffic ==> MT
78
+
79
+ MT --> CDC[CortexDataClient]:::mesh
80
+
81
+ Topaz[Zero-Trust Policies] -. Invisible Enforcement .-> MT:::platform
82
+ Langfuse[Distributed Tracing] -. Invisible Telemetry .-> MT:::platform
83
+ end
84
+
85
+ style DIY fill:#cccccc,stroke:#666,stroke-width:1px,stroke-dasharray: 5 5
86
+ style MESH fill:#b8c8d8,stroke:#666,stroke-width:1px,stroke-dasharray: 5 5
87
+ ```
88
+
89
+ ### The SV-1: Systems Interface Description
90
+ How existing legacy architectures (LangChain/LlamaIndex) interface securely with the central orchestrator via structured passthrough.
91
+
92
+ ```mermaid
93
+ %%{init: { 'sequence': { 'boxBorderColor': '#666', 'showSequenceNumbers': true } } }%%
94
+ sequenceDiagram
95
+ autonumber
96
+ box rgba(0, 100, 255, 0.1) Platform Control Plane
97
+ participant U as Custom UI / User
98
+ participant EA as Engine A (Orchestrator)
99
+ end
100
+
101
+ box rgba(0, 200, 0, 0.1) Domain Node (Data Science)
102
+ participant MT as MeshTool (Pydantic Ingress)
103
+ participant LR as Legacy Agent (LangChain/LlamaIndex)
104
+ end
105
+
106
+ box rgba(255, 150, 0, 0.1) Platform Data Plane
107
+ participant DP as CortexDataClient
108
+ end
109
+
110
+ U->>EA: "Hey, check delays for supplier X and summarize"
111
+ Note over EA: Central LLM applies NLP Parsing<br/>& Intent Classification
112
+ EA->>MT: Validated Pydantic Object<br/>{intent: "delay_check", id: "X"}
113
+ MT->>LR: Clean Variables
114
+ Note over LR: Executes existing custom logic<br/>(No NLP parsing needed locally!)
115
+ LR->>DP: get_dataframe(urn:...)
116
+ DP-->>LR: RLS-Filtered Polars LazyFrame
117
+ LR-->>MT: Analysis Result
118
+ MT-->>EA: Standardized JSON Output
119
+ EA-->>U: Final Formatted Response
120
+ ```
121
+
122
+ ### The SV-4: Systems Functionality / Data Security Flow
123
+ The invisible path of identity propagation ensuring every computation is grounded in verifiable enterprise access controls.
124
+
125
+ ```mermaid
126
+ %%{init: {'theme': 'neutral'} }%%
127
+ flowchart LR
128
+ classDef secure fill:#e8f4f8,stroke:#1b85b8,stroke-width:2px;
129
+ classDef compute fill:#f4e8f8,stroke:#851bb8,stroke-width:2px;
130
+ classDef data fill:#e8f8ec,stroke:#1bb845,stroke-width:2px;
131
+
132
+ User([User Session]) -- 1. Injects JWT --> Jupyter[Jupyter / UI]:::compute
133
+ Jupyter -- 2. mesh.ask() --> EngineA[Engine A]:::compute
134
+ EngineA -- 3. Forwards JWT Header --> MeshTool[MeshTool Node]:::compute
135
+
136
+ subgraph Zero-Trust Perimeter
137
+ MeshTool -- 4. Request Asset --> Gateway[Central Data Gateway]:::secure
138
+ Gateway -- 5. Verify Token --> Keycloak[Keycloak IDP]:::secure
139
+ Gateway -- 6. Fetch Policies --> Topaz[AuthZ Engine]:::secure
140
+ end
141
+
142
+ subgraph Enterprise Data Lake
143
+ Topaz -- 7. Apply RLS & Masking --> S3[(AWS S3 / Database)]:::data
144
+ end
145
+
146
+ S3 -- 8. Mint STS & Stream Data --> MeshTool
147
+
148
+ MeshTool --- Logic[Data Scientist safely computes<br/>on Polars LazyFrame]:::compute
149
+
150
+ style Zero-Trust Perimeter fill:#c5d8e0,stroke:#1b85b8,stroke-dasharray: 5 5
151
+ style Enterprise Data Lake fill:#c5e0ca,stroke:#1bb845,stroke-dasharray: 5 5
152
+ ```
153
+
154
+ ---
155
+
156
+ ## THE VERDICT
157
+
158
+ If you build your own orchestrator and API from scratch, you own the burden of distributed tracing, token rotation, data access policy enforcement, and proving your agents are trustworthy.
159
+
160
+ If you wrap your logic in `@app.execute()`, the Mesh handles 100% of the enterprise plumbing, ensuring your agents are traceable, predictable, observable, and grounded by default.
161
+
162
+ **Focus your talent on building superior agents and exceptional custom UIs. Let the Mesh handle the infrastructure.**