hexgate 0.2.1__tar.gz → 0.2.3__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 (107) hide show
  1. hexgate-0.2.3/LICENSE +21 -0
  2. {hexgate-0.2.1/hexgate.egg-info → hexgate-0.2.3}/PKG-INFO +96 -48
  3. hexgate-0.2.1/PKG-INFO → hexgate-0.2.3/README.md +94 -85
  4. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/google/runner.py +19 -3
  5. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/langchain/agent.py +2 -2
  6. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/langchain/wrapper.py +2 -2
  7. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/openai/runner.py +11 -10
  8. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/pydantic_ai/agent.py +1 -1
  9. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/pydantic_ai/wrapper.py +1 -1
  10. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/agents/loader.py +4 -4
  11. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/_common.py +1 -1
  12. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/register/hexgate.py +2 -2
  13. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/register/main.py +2 -2
  14. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/serve.py +3 -3
  15. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cloud/__init__.py +1 -1
  16. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cloud/attenuate.py +1 -1
  17. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cloud/biscuit.py +1 -1
  18. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cloud/client.py +7 -7
  19. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/security/rego.py +1 -1
  20. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/security/wasm_engine.py +3 -3
  21. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/streaming/__init__.py +1 -1
  22. hexgate-0.2.1/README.md → hexgate-0.2.3/hexgate.egg-info/PKG-INFO +132 -48
  23. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate.egg-info/SOURCES.txt +1 -0
  24. {hexgate-0.2.1 → hexgate-0.2.3}/pyproject.toml +4 -2
  25. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/__init__.py +0 -0
  26. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/__init__.py +0 -0
  27. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/google/__init__.py +0 -0
  28. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/google/tools.py +0 -0
  29. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/google/wrapper.py +0 -0
  30. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/langchain/__init__.py +0 -0
  31. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/langchain/tools.py +0 -0
  32. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/openai/__init__.py +0 -0
  33. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/openai/tools.py +0 -0
  34. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/openai/wrapper.py +0 -0
  35. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/pydantic_ai/__init__.py +0 -0
  36. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/adapters/pydantic_ai/tools.py +0 -0
  37. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/agents/__init__.py +0 -0
  38. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/agents/builtin/__init__.py +0 -0
  39. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/agents/builtin/researcher/agent.yaml +0 -0
  40. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/agents/builtin/researcher/policy.yaml +0 -0
  41. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/agents/builtin/researcher/system.md +0 -0
  42. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/agents/factory.py +0 -0
  43. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/agents/models.py +0 -0
  44. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/agents/prompts/agent_system.md +0 -0
  45. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/audit.py +0 -0
  46. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/bootstrap.py +0 -0
  47. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/__init__.py +0 -0
  48. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/chat.py +0 -0
  49. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/policy/__init__.py +0 -0
  50. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/policy/main.py +0 -0
  51. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/register/__init__.py +0 -0
  52. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/register/google.py +0 -0
  53. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/register/langchain.py +0 -0
  54. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/register/manifest.py +0 -0
  55. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/register/models.py +0 -0
  56. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/register/openai.py +0 -0
  57. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/register/pydantic_ai.py +0 -0
  58. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/register/register.py +0 -0
  59. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/cli/state.py +0 -0
  60. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/config/__init__.py +0 -0
  61. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/config/settings.py +0 -0
  62. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/runtime/__init__.py +0 -0
  63. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/runtime/command_policy.py +0 -0
  64. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/runtime/context.py +0 -0
  65. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/runtime/sandbox_runtime.py +0 -0
  66. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/runtime/srt.py +0 -0
  67. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/runtime/workspace.py +0 -0
  68. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/security/__init__.py +0 -0
  69. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/security/binding.py +0 -0
  70. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/security/bundle.py +0 -0
  71. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/security/constraints.py +0 -0
  72. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/security/decision.py +0 -0
  73. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/security/enforcer.py +0 -0
  74. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/security/errors.py +0 -0
  75. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/security/file_scope.py +0 -0
  76. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/security/models.py +0 -0
  77. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/security/policy.py +0 -0
  78. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/security/policy_set.py +0 -0
  79. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/security/rego_wasm.py +0 -0
  80. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/security/signing.py +0 -0
  81. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/security/source.py +0 -0
  82. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/streaming/events.py +0 -0
  83. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/streaming/normalize.py +0 -0
  84. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/tools/__init__.py +0 -0
  85. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/tools/bash.py +0 -0
  86. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/tools/decorators.py +0 -0
  87. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/tools/fetch.py +0 -0
  88. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/tools/files/__init__.py +0 -0
  89. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/tools/files/_common.py +0 -0
  90. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/tools/files/edit_file.py +0 -0
  91. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/tools/files/glob.py +0 -0
  92. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/tools/files/grep.py +0 -0
  93. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/tools/files/read_file.py +0 -0
  94. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/tools/files/write_file.py +0 -0
  95. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/tools/refund.py +0 -0
  96. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/tools/websearch.py +0 -0
  97. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/tracing/__init__.py +0 -0
  98. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/tracing/langfuse.py +0 -0
  99. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/utils/__init__.py +0 -0
  100. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate/utils/retry.py +0 -0
  101. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate.egg-info/dependency_links.txt +0 -0
  102. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate.egg-info/entry_points.txt +0 -0
  103. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate.egg-info/requires.txt +0 -0
  104. {hexgate-0.2.1 → hexgate-0.2.3}/hexgate.egg-info/top_level.txt +0 -0
  105. {hexgate-0.2.1 → hexgate-0.2.3}/setup.cfg +0 -0
  106. {hexgate-0.2.1 → hexgate-0.2.3}/tests/test_bootstrap.py +0 -0
  107. {hexgate-0.2.1 → hexgate-0.2.3}/tests/test_demo.py +0 -0
hexgate-0.2.3/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hexamind
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 without 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.
@@ -1,9 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hexgate
3
- Version: 0.2.1
4
- Summary: HexaGate — authorization infrastructure for AI agents (agent runtime + cloud client).
3
+ Version: 0.2.3
4
+ Summary: Hexgate — authorization infrastructure for AI agents (agent runtime + cloud client).
5
+ License-Expression: MIT
5
6
  Requires-Python: >=3.13
6
7
  Description-Content-Type: text/markdown
8
+ License-File: LICENSE
7
9
  Requires-Dist: bashlex>=0.18
8
10
  Requires-Dist: biscuit-python>=0.4
9
11
  Requires-Dist: cryptography>=42
@@ -33,21 +35,86 @@ Requires-Dist: jupyter; extra == "dev"
33
35
  Requires-Dist: pytest>=8.4.1; extra == "dev"
34
36
  Requires-Dist: pytest-asyncio>=1.0.0; extra == "dev"
35
37
  Requires-Dist: ruff>=0.12.2; extra == "dev"
38
+ Dynamic: license-file
36
39
 
37
- # hexgate
40
+ <div align="center">
38
41
 
39
- `hexgate` is a lightweight LangChain-based agent runtime built around:
42
+ <img src="./icon.svg" alt="Hexgate" width="96" height="96" />
40
43
 
41
- - `langchain`
42
- - `gpt-5.4`
43
- - `Linkup` web search
44
- - Tavily-based page fetch
45
- - `Langfuse` tracing
44
+ # Hexgate
46
45
 
47
- This package is intentionally small. The first milestone is a single assistant with:
46
+ **Authorization infrastructure for AI agents.**
47
+ Policy enforcement, signed policy bundles, per-request user scope, audit trail — for OpenAI Agents, LangChain, Google ADK, Pydantic AI, or a native runtime.
48
48
 
49
- - `web_search`
50
- - `fetch`
49
+ [**Website**](https://hexgate.ai) · [Docs](https://docs.hexgate.ai) · [PyPI](https://pypi.org/project/hexgate/) · [Discussions](https://github.com/HexamindOrganisation/hexgate/discussions)
50
+
51
+ [![PyPI](https://img.shields.io/pypi/v/hexgate?color=blue&logo=pypi&logoColor=white)](https://pypi.org/project/hexgate/)
52
+ [![CI](https://github.com/HexamindOrganisation/hexgate/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/HexamindOrganisation/hexgate/actions/workflows/tests.yml)
53
+ [![Downloads](https://img.shields.io/pypi/dm/hexgate?color=blueviolet)](https://pypi.org/project/hexgate/)
54
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
55
+
56
+ <br />
57
+
58
+ <img src="./assets/hero.png" alt="Control what your agents do — not just what they say. Policy decisions streaming live from the PolicyEnforcer." />
59
+
60
+ <br />
61
+
62
+ [Quick Start](#-quick-start--local-cli) · [Two paths](#-which-path-do-i-pick) · [Framework adapters](#-framework-agent-wrapping) · [Policy bundles](#-policy-bundles--compile-sign-enforce-wasm) · [User scope](#-user-scope--roles) · [Platform](#-hexgate-platform)
63
+
64
+ </div>
65
+
66
+ ---
67
+
68
+ ## What is Hexgate?
69
+
70
+ Hexgate is two things that move together:
71
+
72
+ - **`hexgate` — the SDK.** A Python runtime that gates every tool call through a typed `Decision` (allow / deny / approval-required), wraps your existing OpenAI / LangChain / Google ADK / Pydantic AI agent without rewriting it, and threads per-request user identity through tracing + audit.
73
+ - **The Hexgate platform** *(optional)* — a FastAPI control plane + React dashboard for editing policy in a browser, minting per-project tokens, watching live decisions stream from a serving agent, and shipping signed WASM policy bundles to production.
74
+
75
+ You can use the SDK with nothing else (single-process REPL, YAML on disk). Or plug in the platform when you want auditable decisions in ClickHouse, a shared Playground UI, and live policy edits.
76
+
77
+ ```text
78
+ ┌─────────────────────────────────────────┐
79
+ your code ───► │ create_agent / wrap_*_agent / Runner │
80
+ │ ↓ │
81
+ │ PolicyEnforcer.decide(role, tool) │
82
+ │ ↓ │
83
+ │ allow · deny · approval_required │
84
+ └────────────────────┬────────────────────┘
85
+
86
+ ┌────────────────────────┼─────────────────────────┐
87
+ ▼ ▼ ▼
88
+ ┌────────────────┐ ┌──────────────────┐ ┌────────────────┐
89
+ │ Local policy │ │ Signed WASM │ │ Audit log │
90
+ │ (YAML / dir, │ │ bundle from │ │ (ClickHouse │
91
+ │ hot reload) │ │ Hexgate cloud │ │ via REST) │
92
+ └────────────────┘ └──────────────────┘ └────────────────┘
93
+ ```
94
+
95
+ ## Table of contents
96
+
97
+ - [Prerequisites](#-prerequisites)
98
+ - [Quick Start — Local CLI](#-quick-start--local-cli)
99
+ - [Which path do I pick?](#-which-path-do-i-pick) — chat vs serve
100
+ - [Quick Start — Platform](#-quick-start--platform)
101
+ - [Core primitives](#-core-primitives)
102
+ - [Build an agent — end to end](#-build-an-agent--end-to-end)
103
+ - [What you can import](#-what-you-can-import)
104
+ - [Framework agent wrapping](#-framework-agent-wrapping) — OpenAI, LangChain, Google ADK, Pydantic AI
105
+ - [Define agents in code](#-define-agents-in-code)
106
+ - [Builtin and local agents](#-builtin-and-local-agents)
107
+ - [Policy shape](#-policy-shape)
108
+ - [Tool-call policy enforcement](#-tool-call-policy-enforcement)
109
+ - [Policy bundles — compile, sign, enforce (WASM)](#-policy-bundles--compile-sign-enforce-wasm)
110
+ - [Approval-required tool calls](#-approval-required-tool-calls)
111
+ - [Workspace sandbox](#-workspace-sandbox)
112
+ - [Environment](#-environment)
113
+ - [Tests & dev tooling](#-tests--dev-tooling)
114
+ - [CLI reference](#-cli-reference)
115
+ - [Hexgate platform](#-hexgate-platform)
116
+ - [User scope + roles](#-user-scope--roles)
117
+ - [Stream results](#-stream-results)
51
118
 
52
119
  ## 🛠️ Prerequisites
53
120
 
@@ -119,7 +186,7 @@ Both commands accept either a plain agent id (`--agent researcher`) or a uvicorn
119
186
 
120
187
  ## 🚀 Quick Start — Platform
121
188
 
122
- To run the full HexaGate control plane locally (FastAPI backend + dashboard + your local agent serving over WebSocket), you need **three terminals**. The Makefile has a target that prints the recipe:
189
+ To run the full Hexgate control plane locally (FastAPI backend + dashboard + your local agent serving over WebSocket), you need **three terminals**. The Makefile has a target that prints the recipe:
123
190
 
124
191
  ```bash
125
192
  make demo-platform # prints the 3-terminal recipe below
@@ -602,7 +669,7 @@ What happens under the hood:
602
669
 
603
670
  Working scripts in `examples/`:
604
671
 
605
- - `examples/customer_bot.py` — canonical HexaGate path: `create_agent(...)` + the dashboard register/serve loop end-to-end.
672
+ - `examples/customer_bot.py` — canonical Hexgate path: `create_agent(...)` + the dashboard register/serve loop end-to-end.
606
673
  - `examples/openai_demo.py` — `HexgateRunner` (OpenAI Agents SDK) end-to-end.
607
674
  - `examples/google_demo.py` — `HexgateRunner` (Google ADK) end-to-end with `InMemorySessionService`.
608
675
  - `examples/pydantic_ai_demo.py` — `wrap_pydantic_agent` (Pydantic AI) end-to-end.
@@ -737,7 +804,7 @@ That means the same agent code can stay simple in development, while deployment
737
804
 
738
805
  ## 🧩 Policy Bundles — Compile, Sign, Enforce (WASM)
739
806
 
740
- HexaGate has **two policy enforcement engines** that return identical decisions (there's a parity test suite that proves it):
807
+ Hexgate has **two policy enforcement engines** that return identical decisions (there's a parity test suite that proves it):
741
808
 
742
809
  - **pydantic** (default) — evaluates constraints in-process. Zero setup; this is what every example above uses.
743
810
  - **WASM** — compiles `policy.yaml` → Rego → a WebAssembly module evaluated via `wasmtime`. This is the path production ships: one compiled artifact, byte-for-byte reproducible, cryptographically signed by the platform.
@@ -1059,44 +1126,21 @@ The platform-side test suite is separate and lives at `platform/api/tests/`:
1059
1126
  cd platform/api && uv run pytest tests/
1060
1127
  ```
1061
1128
 
1062
- ## ▶️ Run It
1063
-
1064
- Install the package into your current environment:
1065
-
1066
- ```bash
1067
- python -m pip install -e .
1068
- ```
1069
-
1070
- Run the config-driven demo:
1071
-
1072
- ```bash
1073
- python examples/demo.py
1074
- ```
1075
-
1076
- Run the inline chat CLI with a local or builtin YAML agent:
1077
-
1078
- ```bash
1079
- hexgate chat --agent example_agent
1080
- ```
1129
+ ## 🖥️ CLI reference
1081
1130
 
1082
- Run the CLI with code-defined agents from a Python script:
1131
+ The `hexgate` binary exposes `chat`, `serve`, `register`, and `policy` subcommands. The [Quick Start](#-quick-start--local-cli) covers `chat`; this section drills into `register` and `serve` for the platform path.
1083
1132
 
1084
1133
  ```bash
1134
+ hexgate --help # list subcommands
1135
+ hexgate <subcommand> --help # flags for a subcommand
1136
+ hexgate chat --list-agents # show resolvable agents
1085
1137
  hexgate chat --use examples/file_agents.py --agent workspace_explorer
1086
- hexgate chat --use examples/file_agents.py --agent repo_editor
1087
- hexgate chat --use examples/research_agents.py --agent update_researcher
1088
1138
  hexgate chat --use examples/research_agents.py --agent update_researcher --approval-mode ask
1089
1139
  ```
1090
1140
 
1091
- List what the CLI can currently resolve:
1092
-
1093
- ```bash
1094
- hexgate chat --list-agents
1095
- ```
1096
-
1097
1141
  ### `hexgate register` — push a manifest to the platform
1098
1142
 
1099
- Register a code-defined agent's manifest with the HexaGate platform. `--agent`
1143
+ Register a code-defined agent's manifest with the Hexgate platform. `--agent`
1100
1144
  takes a Python import path of the form `module.path:attribute`, the same shape
1101
1145
  as ASGI/WSGI entrypoints. The CLI imports the module, grabs the agent object,
1102
1146
  and POSTs its manifest to `${HEXGATE_API_URL}/v1/agents` using
@@ -1135,7 +1179,7 @@ system prompt directly off the object. No flags needed.
1135
1179
  `--system-prompt` accepts either a literal string or a path to a `.md` /
1136
1180
  `.txt` / `.jinja` file (read as text at register time).
1137
1181
 
1138
- Supported frameworks: OpenAI Agents SDK, Google ADK, Pydantic AI, LangChain/LangGraph, HexaGate agents.
1182
+ Supported frameworks: OpenAI Agents SDK, Google ADK, Pydantic AI, LangChain/LangGraph, Hexgate agents.
1139
1183
 
1140
1184
  ### `hexgate serve` — bridge a local agent to the platform's relay
1141
1185
 
@@ -1172,7 +1216,7 @@ print(manifest.model_dump())
1172
1216
  ```
1173
1217
 
1174
1218
  `create_manifest` dispatches on the framework of `agent`. The supported
1175
- types are the same set `hexgate register` accepts: HexaGate, OpenAI Agents
1219
+ types are the same set `hexgate register` accepts: Hexgate, OpenAI Agents
1176
1220
  SDK, Google ADK, Pydantic AI, and LangChain/LangGraph compiled graphs.
1177
1221
  For LangGraph you must pass `tools=` explicitly, and may pass `model=` /
1178
1222
  `system_prompt=`, since compiled graphs don't expose those fields after
@@ -1182,7 +1226,7 @@ The return value is an `AgentManifest` (a Pydantic model, also re-exported
1182
1226
  from `hexgate` for type annotations) — the same schema the platform
1183
1227
  stores and the dashboard renders.
1184
1228
 
1185
- ## 🌐 HexaGate Platform
1229
+ ## 🌐 Hexgate Platform
1186
1230
 
1187
1231
  The `platform/` directory contains an optional control plane that hosts agent definitions, dev tokens, and a live debug surface. The SDK works fully without it (`load_local_agent`, `load_builtin_agent` keep their existing semantics) — but with it you get:
1188
1232
 
@@ -1301,7 +1345,7 @@ the name from the loaded agent's `.name` attribute — no env var needed.
1301
1345
 
1302
1346
  ## 👤 User Scope + Roles
1303
1347
 
1304
- Real backends serve many users, and different users get different capabilities. HexaGate splits that into two pieces:
1348
+ Real backends serve many users, and different users get different capabilities. Hexgate splits that into two pieces:
1305
1349
 
1306
1350
  - **`User`** — the per-request scope. Marks "this invocation acts on behalf of alice, in role X." Async context manager; pushes a fact-bearing Biscuit through the agent runtime.
1307
1351
  - **Role policies** — one `policy.yaml` per role, optionally inheriting from a base mixin. The runtime picks the right one at call time based on the active `User.role`.
@@ -1451,3 +1495,7 @@ async for event in stream_agent(agent, handler, "latest AI breakthroughs"):
1451
1495
  - assistant text deltas
1452
1496
  - tool lifecycle
1453
1497
  - final run completion
1498
+
1499
+ ---
1500
+
1501
+ If Hexgate looks useful, [give it a ⭐ on GitHub](https://github.com/HexamindOrganisation/hexgate) — it helps more than you'd think. Built by [Hexamind](https://hexgate.ai).
@@ -1,53 +1,81 @@
1
- Metadata-Version: 2.4
2
- Name: hexgate
3
- Version: 0.2.1
4
- Summary: HexaGate — authorization infrastructure for AI agents (agent runtime + cloud client).
5
- Requires-Python: >=3.13
6
- Description-Content-Type: text/markdown
7
- Requires-Dist: bashlex>=0.18
8
- Requires-Dist: biscuit-python>=0.4
9
- Requires-Dist: cryptography>=42
10
- Requires-Dist: httpx>=0.28.1
11
- Requires-Dist: langchain
12
- Requires-Dist: langchain-openai
13
- Requires-Dist: langchain-core
14
- Requires-Dist: langfuse
15
- Requires-Dist: pydantic>=2.12.4
16
- Requires-Dist: python-dotenv>=1.1.1
17
- Requires-Dist: pyyaml>=6.0.2
18
- Requires-Dist: rich>=13.9.4
19
- Requires-Dist: websockets>=13.0
20
- Requires-Dist: openai-agents>=0.0.10
21
- Requires-Dist: langgraph>=0.2
22
- Requires-Dist: nest_asyncio>=1.6
23
- Requires-Dist: openinference-instrumentation-openai-agents>=0.1
24
- Requires-Dist: google-adk>=1.0
25
- Requires-Dist: google-genai>=1.0
26
- Requires-Dist: litellm>=1.50
27
- Requires-Dist: openinference-instrumentation-google-adk>=0.1.11
28
- Requires-Dist: pydantic-ai-slim>=1.88.0
29
- Requires-Dist: wasmtime>=20.0
30
- Provides-Extra: dev
31
- Requires-Dist: ipykernel; extra == "dev"
32
- Requires-Dist: jupyter; extra == "dev"
33
- Requires-Dist: pytest>=8.4.1; extra == "dev"
34
- Requires-Dist: pytest-asyncio>=1.0.0; extra == "dev"
35
- Requires-Dist: ruff>=0.12.2; extra == "dev"
36
-
37
- # hexgate
38
-
39
- `hexgate` is a lightweight LangChain-based agent runtime built around:
40
-
41
- - `langchain`
42
- - `gpt-5.4`
43
- - `Linkup` web search
44
- - Tavily-based page fetch
45
- - `Langfuse` tracing
46
-
47
- This package is intentionally small. The first milestone is a single assistant with:
1
+ <div align="center">
48
2
 
49
- - `web_search`
50
- - `fetch`
3
+ <img src="./icon.svg" alt="Hexgate" width="96" height="96" />
4
+
5
+ # Hexgate
6
+
7
+ **Authorization infrastructure for AI agents.**
8
+ Policy enforcement, signed policy bundles, per-request user scope, audit trail — for OpenAI Agents, LangChain, Google ADK, Pydantic AI, or a native runtime.
9
+
10
+ [**Website**](https://hexgate.ai) · [Docs](https://docs.hexgate.ai) · [PyPI](https://pypi.org/project/hexgate/) · [Discussions](https://github.com/HexamindOrganisation/hexgate/discussions)
11
+
12
+ [![PyPI](https://img.shields.io/pypi/v/hexgate?color=blue&logo=pypi&logoColor=white)](https://pypi.org/project/hexgate/)
13
+ [![CI](https://github.com/HexamindOrganisation/hexgate/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/HexamindOrganisation/hexgate/actions/workflows/tests.yml)
14
+ [![Downloads](https://img.shields.io/pypi/dm/hexgate?color=blueviolet)](https://pypi.org/project/hexgate/)
15
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
16
+
17
+ <br />
18
+
19
+ <img src="./assets/hero.png" alt="Control what your agents do — not just what they say. Policy decisions streaming live from the PolicyEnforcer." />
20
+
21
+ <br />
22
+
23
+ [Quick Start](#-quick-start--local-cli) · [Two paths](#-which-path-do-i-pick) · [Framework adapters](#-framework-agent-wrapping) · [Policy bundles](#-policy-bundles--compile-sign-enforce-wasm) · [User scope](#-user-scope--roles) · [Platform](#-hexgate-platform)
24
+
25
+ </div>
26
+
27
+ ---
28
+
29
+ ## What is Hexgate?
30
+
31
+ Hexgate is two things that move together:
32
+
33
+ - **`hexgate` — the SDK.** A Python runtime that gates every tool call through a typed `Decision` (allow / deny / approval-required), wraps your existing OpenAI / LangChain / Google ADK / Pydantic AI agent without rewriting it, and threads per-request user identity through tracing + audit.
34
+ - **The Hexgate platform** *(optional)* — a FastAPI control plane + React dashboard for editing policy in a browser, minting per-project tokens, watching live decisions stream from a serving agent, and shipping signed WASM policy bundles to production.
35
+
36
+ You can use the SDK with nothing else (single-process REPL, YAML on disk). Or plug in the platform when you want auditable decisions in ClickHouse, a shared Playground UI, and live policy edits.
37
+
38
+ ```text
39
+ ┌─────────────────────────────────────────┐
40
+ your code ───► │ create_agent / wrap_*_agent / Runner │
41
+ │ ↓ │
42
+ │ PolicyEnforcer.decide(role, tool) │
43
+ │ ↓ │
44
+ │ allow · deny · approval_required │
45
+ └────────────────────┬────────────────────┘
46
+
47
+ ┌────────────────────────┼─────────────────────────┐
48
+ ▼ ▼ ▼
49
+ ┌────────────────┐ ┌──────────────────┐ ┌────────────────┐
50
+ │ Local policy │ │ Signed WASM │ │ Audit log │
51
+ │ (YAML / dir, │ │ bundle from │ │ (ClickHouse │
52
+ │ hot reload) │ │ Hexgate cloud │ │ via REST) │
53
+ └────────────────┘ └──────────────────┘ └────────────────┘
54
+ ```
55
+
56
+ ## Table of contents
57
+
58
+ - [Prerequisites](#-prerequisites)
59
+ - [Quick Start — Local CLI](#-quick-start--local-cli)
60
+ - [Which path do I pick?](#-which-path-do-i-pick) — chat vs serve
61
+ - [Quick Start — Platform](#-quick-start--platform)
62
+ - [Core primitives](#-core-primitives)
63
+ - [Build an agent — end to end](#-build-an-agent--end-to-end)
64
+ - [What you can import](#-what-you-can-import)
65
+ - [Framework agent wrapping](#-framework-agent-wrapping) — OpenAI, LangChain, Google ADK, Pydantic AI
66
+ - [Define agents in code](#-define-agents-in-code)
67
+ - [Builtin and local agents](#-builtin-and-local-agents)
68
+ - [Policy shape](#-policy-shape)
69
+ - [Tool-call policy enforcement](#-tool-call-policy-enforcement)
70
+ - [Policy bundles — compile, sign, enforce (WASM)](#-policy-bundles--compile-sign-enforce-wasm)
71
+ - [Approval-required tool calls](#-approval-required-tool-calls)
72
+ - [Workspace sandbox](#-workspace-sandbox)
73
+ - [Environment](#-environment)
74
+ - [Tests & dev tooling](#-tests--dev-tooling)
75
+ - [CLI reference](#-cli-reference)
76
+ - [Hexgate platform](#-hexgate-platform)
77
+ - [User scope + roles](#-user-scope--roles)
78
+ - [Stream results](#-stream-results)
51
79
 
52
80
  ## 🛠️ Prerequisites
53
81
 
@@ -119,7 +147,7 @@ Both commands accept either a plain agent id (`--agent researcher`) or a uvicorn
119
147
 
120
148
  ## 🚀 Quick Start — Platform
121
149
 
122
- To run the full HexaGate control plane locally (FastAPI backend + dashboard + your local agent serving over WebSocket), you need **three terminals**. The Makefile has a target that prints the recipe:
150
+ To run the full Hexgate control plane locally (FastAPI backend + dashboard + your local agent serving over WebSocket), you need **three terminals**. The Makefile has a target that prints the recipe:
123
151
 
124
152
  ```bash
125
153
  make demo-platform # prints the 3-terminal recipe below
@@ -602,7 +630,7 @@ What happens under the hood:
602
630
 
603
631
  Working scripts in `examples/`:
604
632
 
605
- - `examples/customer_bot.py` — canonical HexaGate path: `create_agent(...)` + the dashboard register/serve loop end-to-end.
633
+ - `examples/customer_bot.py` — canonical Hexgate path: `create_agent(...)` + the dashboard register/serve loop end-to-end.
606
634
  - `examples/openai_demo.py` — `HexgateRunner` (OpenAI Agents SDK) end-to-end.
607
635
  - `examples/google_demo.py` — `HexgateRunner` (Google ADK) end-to-end with `InMemorySessionService`.
608
636
  - `examples/pydantic_ai_demo.py` — `wrap_pydantic_agent` (Pydantic AI) end-to-end.
@@ -737,7 +765,7 @@ That means the same agent code can stay simple in development, while deployment
737
765
 
738
766
  ## 🧩 Policy Bundles — Compile, Sign, Enforce (WASM)
739
767
 
740
- HexaGate has **two policy enforcement engines** that return identical decisions (there's a parity test suite that proves it):
768
+ Hexgate has **two policy enforcement engines** that return identical decisions (there's a parity test suite that proves it):
741
769
 
742
770
  - **pydantic** (default) — evaluates constraints in-process. Zero setup; this is what every example above uses.
743
771
  - **WASM** — compiles `policy.yaml` → Rego → a WebAssembly module evaluated via `wasmtime`. This is the path production ships: one compiled artifact, byte-for-byte reproducible, cryptographically signed by the platform.
@@ -1059,44 +1087,21 @@ The platform-side test suite is separate and lives at `platform/api/tests/`:
1059
1087
  cd platform/api && uv run pytest tests/
1060
1088
  ```
1061
1089
 
1062
- ## ▶️ Run It
1090
+ ## 🖥️ CLI reference
1063
1091
 
1064
- Install the package into your current environment:
1065
-
1066
- ```bash
1067
- python -m pip install -e .
1068
- ```
1069
-
1070
- Run the config-driven demo:
1071
-
1072
- ```bash
1073
- python examples/demo.py
1074
- ```
1075
-
1076
- Run the inline chat CLI with a local or builtin YAML agent:
1077
-
1078
- ```bash
1079
- hexgate chat --agent example_agent
1080
- ```
1081
-
1082
- Run the CLI with code-defined agents from a Python script:
1092
+ The `hexgate` binary exposes `chat`, `serve`, `register`, and `policy` subcommands. The [Quick Start](#-quick-start--local-cli) covers `chat`; this section drills into `register` and `serve` for the platform path.
1083
1093
 
1084
1094
  ```bash
1095
+ hexgate --help # list subcommands
1096
+ hexgate <subcommand> --help # flags for a subcommand
1097
+ hexgate chat --list-agents # show resolvable agents
1085
1098
  hexgate chat --use examples/file_agents.py --agent workspace_explorer
1086
- hexgate chat --use examples/file_agents.py --agent repo_editor
1087
- hexgate chat --use examples/research_agents.py --agent update_researcher
1088
1099
  hexgate chat --use examples/research_agents.py --agent update_researcher --approval-mode ask
1089
1100
  ```
1090
1101
 
1091
- List what the CLI can currently resolve:
1092
-
1093
- ```bash
1094
- hexgate chat --list-agents
1095
- ```
1096
-
1097
1102
  ### `hexgate register` — push a manifest to the platform
1098
1103
 
1099
- Register a code-defined agent's manifest with the HexaGate platform. `--agent`
1104
+ Register a code-defined agent's manifest with the Hexgate platform. `--agent`
1100
1105
  takes a Python import path of the form `module.path:attribute`, the same shape
1101
1106
  as ASGI/WSGI entrypoints. The CLI imports the module, grabs the agent object,
1102
1107
  and POSTs its manifest to `${HEXGATE_API_URL}/v1/agents` using
@@ -1135,7 +1140,7 @@ system prompt directly off the object. No flags needed.
1135
1140
  `--system-prompt` accepts either a literal string or a path to a `.md` /
1136
1141
  `.txt` / `.jinja` file (read as text at register time).
1137
1142
 
1138
- Supported frameworks: OpenAI Agents SDK, Google ADK, Pydantic AI, LangChain/LangGraph, HexaGate agents.
1143
+ Supported frameworks: OpenAI Agents SDK, Google ADK, Pydantic AI, LangChain/LangGraph, Hexgate agents.
1139
1144
 
1140
1145
  ### `hexgate serve` — bridge a local agent to the platform's relay
1141
1146
 
@@ -1172,7 +1177,7 @@ print(manifest.model_dump())
1172
1177
  ```
1173
1178
 
1174
1179
  `create_manifest` dispatches on the framework of `agent`. The supported
1175
- types are the same set `hexgate register` accepts: HexaGate, OpenAI Agents
1180
+ types are the same set `hexgate register` accepts: Hexgate, OpenAI Agents
1176
1181
  SDK, Google ADK, Pydantic AI, and LangChain/LangGraph compiled graphs.
1177
1182
  For LangGraph you must pass `tools=` explicitly, and may pass `model=` /
1178
1183
  `system_prompt=`, since compiled graphs don't expose those fields after
@@ -1182,7 +1187,7 @@ The return value is an `AgentManifest` (a Pydantic model, also re-exported
1182
1187
  from `hexgate` for type annotations) — the same schema the platform
1183
1188
  stores and the dashboard renders.
1184
1189
 
1185
- ## 🌐 HexaGate Platform
1190
+ ## 🌐 Hexgate Platform
1186
1191
 
1187
1192
  The `platform/` directory contains an optional control plane that hosts agent definitions, dev tokens, and a live debug surface. The SDK works fully without it (`load_local_agent`, `load_builtin_agent` keep their existing semantics) — but with it you get:
1188
1193
 
@@ -1301,7 +1306,7 @@ the name from the loaded agent's `.name` attribute — no env var needed.
1301
1306
 
1302
1307
  ## 👤 User Scope + Roles
1303
1308
 
1304
- Real backends serve many users, and different users get different capabilities. HexaGate splits that into two pieces:
1309
+ Real backends serve many users, and different users get different capabilities. Hexgate splits that into two pieces:
1305
1310
 
1306
1311
  - **`User`** — the per-request scope. Marks "this invocation acts on behalf of alice, in role X." Async context manager; pushes a fact-bearing Biscuit through the agent runtime.
1307
1312
  - **Role policies** — one `policy.yaml` per role, optionally inheriting from a base mixin. The runtime picks the right one at call time based on the active `User.role`.
@@ -1451,3 +1456,7 @@ async for event in stream_agent(agent, handler, "latest AI breakthroughs"):
1451
1456
  - assistant text deltas
1452
1457
  - tool lifecycle
1453
1458
  - final run completion
1459
+
1460
+ ---
1461
+
1462
+ If Hexgate looks useful, [give it a ⭐ on GitHub](https://github.com/HexamindOrganisation/hexgate) — it helps more than you'd think. Built by [Hexamind](https://hexgate.ai).
@@ -21,7 +21,7 @@ from hexgate.runtime import User
21
21
 
22
22
 
23
23
  class HexgateRunner:
24
- """Runner for Google ADK agents with HexaGate tool policy and observability."""
24
+ """Runner for Google ADK agents with Hexgate tool policy and observability."""
25
25
 
26
26
  def __init__(
27
27
  self,
@@ -79,16 +79,32 @@ class HexgateRunner:
79
79
  user: User,
80
80
  **kwargs: Any,
81
81
  ) -> Generator[Any, None, None]:
82
- """Run the Google ADK agent synchronously, yielding events."""
82
+ """Run the Google ADK agent synchronously, yielding events.
83
+
84
+ ADK's ``Runner.run`` drives the agent loop in a worker thread whose
85
+ context cannot see our :class:`User` scope, so the tools' enforcers
86
+ lose the active role. We drive ``run_async`` inline on a per-call loop
87
+ instead, keeping execution in this scoped thread.
88
+ """
83
89
  self._setup_observability()
84
90
  self._binding.refresh() # per-run policy pull; 304 when unchanged
85
91
  with user.sync_scope(), self._propagate(user):
86
- yield from self._runner.run(
92
+ agen = self._runner.run_async(
87
93
  user_id=user.user_id,
88
94
  session_id=user.session_id,
89
95
  new_message=new_message,
90
96
  **kwargs,
91
97
  )
98
+ loop = asyncio.new_event_loop()
99
+ try:
100
+ while True:
101
+ try:
102
+ yield loop.run_until_complete(agen.__anext__())
103
+ except StopAsyncIteration:
104
+ break
105
+ finally:
106
+ loop.run_until_complete(agen.aclose())
107
+ loop.close()
92
108
 
93
109
  async def run_async(
94
110
  self,
@@ -1,4 +1,4 @@
1
- """Proxy around a pre-built ``CompiledStateGraph`` for HexaGate-aware calls."""
1
+ """Proxy around a pre-built ``CompiledStateGraph`` for Hexgate-aware calls."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -60,7 +60,7 @@ class HexgateLangchainAgent:
60
60
  }
61
61
 
62
62
  def _with_callbacks(self, config: RunnableConfig | None) -> RunnableConfig:
63
- """Append the HexaGate callback handler to ``config['callbacks']``."""
63
+ """Append the Hexgate callback handler to ``config['callbacks']``."""
64
64
  merged: RunnableConfig = dict(config) if config else {}
65
65
  callbacks = list(merged.get("callbacks") or [])
66
66
  if self._callback_handler not in callbacks:
@@ -1,5 +1,5 @@
1
1
  """BYO-graph entry point: retrofit a pre-built ``CompiledStateGraph`` with
2
- HexaGate policy. Tools are mutated in place so the graph keeps its
2
+ Hexgate policy. Tools are mutated in place so the graph keeps its
3
3
  references; the returned :class:`HexgateLangchainAgent` opens a User
4
4
  scope + Langfuse propagation per call. For the manifest-driven path,
5
5
  use :func:`hexgate.enforce_policy` instead.
@@ -28,7 +28,7 @@ def wrap_langchain_agent(
28
28
  tools: list[BaseTool],
29
29
  api_key: str | None = None,
30
30
  ) -> HexgateLangchainAgent:
31
- """Wrap a pre-built LangGraph agent with HexaGate policy enforcement.
31
+ """Wrap a pre-built LangGraph agent with Hexgate policy enforcement.
32
32
 
33
33
  Mutates ``tools`` in place so the graph keeps its references.
34
34
  The returned proxy takes ``user`` per invocation; role resolves at
@@ -32,7 +32,7 @@ from hexgate.security.enforcer import build_enforcer
32
32
 
33
33
 
34
34
  class HexgateRunner:
35
- """Runner for OpenAI agents with HexaGate tool policy and observability."""
35
+ """Runner for OpenAI agents with Hexgate tool policy and observability."""
36
36
 
37
37
  def __init__(self, api_key: str | None = None):
38
38
  self.api_key = api_key or os.getenv("HEXGATE_KEY")
@@ -134,21 +134,22 @@ class HexgateRunner:
134
134
  ) -> RunResultStreaming:
135
135
  """Stream the OpenAI agent inside a User scope.
136
136
 
137
- ``Runner.run_streamed`` returns sync; tools only run during
138
- ``stream_events`` iteration. The User scope is opened inside the
139
- wrapped iterator. Langfuse propagation runs during setup so the
140
- trace span attaches. The policy refresh runs in the setup body
141
- the wrap is fixed before tools fire during stream_events.
137
+ ``Runner.run_streamed`` returns sync but spawns the agent loop as a
138
+ background task that snapshots the current contextvars at creation;
139
+ tools fire there, not in ``stream_events``. So the User scope must be
140
+ active around the ``run_streamed`` call for the task to inherit it
141
+ the wrapped iterator re-opens it for exit/audit semantics.
142
142
  """
143
143
  self._setup_observability()
144
144
  binding = self._binding_for(agent)
145
145
  binding.refresh() # must precede the wrap + setup
146
146
  wrapped_agent = wrap_openai_agent(agent, enforcer=binding.enforcer)
147
147
 
148
- with self._propagate(user, agent.name):
149
- result = Runner.run_streamed(
150
- wrapped_agent, input, run_config=run_config, **kwargs
151
- )
148
+ with user.sync_scope():
149
+ with self._propagate(user, agent.name):
150
+ result = Runner.run_streamed(
151
+ wrapped_agent, input, run_config=run_config, **kwargs
152
+ )
152
153
 
153
154
  original_stream_events = result.stream_events
154
155
 
@@ -1,4 +1,4 @@
1
- """Proxy around a pydantic_ai ``Agent`` for HexaGate-aware calls."""
1
+ """Proxy around a pydantic_ai ``Agent`` for Hexgate-aware calls."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -49,7 +49,7 @@ def wrap_pydantic_agent(
49
49
  agent: Agent,
50
50
  api_key: str | None = None,
51
51
  ) -> HexgatePydanticAgent:
52
- """Wrap a pydantic_ai agent with HexaGate policy + observability.
52
+ """Wrap a pydantic_ai agent with Hexgate policy + observability.
53
53
 
54
54
  Returns a :class:`HexgatePydanticAgent` backed by a clone of the
55
55
  caller's ``agent``; the original is not mutated. The proxy takes