computer-agent-py 0.1.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.
- computer_agent_py-0.1.0/PKG-INFO +307 -0
- computer_agent_py-0.1.0/README.md +264 -0
- computer_agent_py-0.1.0/pyproject.toml +128 -0
- computer_agent_py-0.1.0/src/computeragent/__init__.py +90 -0
- computer_agent_py-0.1.0/src/computeragent/_proxy/__init__.py +8 -0
- computer_agent_py-0.1.0/src/computeragent/_proxy/client.py +225 -0
- computer_agent_py-0.1.0/src/computeragent/_proxy/query.py +165 -0
- computer_agent_py-0.1.0/src/computeragent/policy/__init__.py +59 -0
- computer_agent_py-0.1.0/src/computeragent/policy/authorizer.py +161 -0
- computer_agent_py-0.1.0/src/computeragent/policy/cedar.py +182 -0
- computer_agent_py-0.1.0/src/computeragent/policy/opa.py +124 -0
- computer_agent_py-0.1.0/src/computeragent/policy/types.py +121 -0
- computer_agent_py-0.1.0/src/computeragent/py.typed +0 -0
- computer_agent_py-0.1.0/src/computeragent/telemetry/__init__.py +24 -0
- computer_agent_py-0.1.0/src/computeragent/telemetry/config.py +176 -0
- computer_agent_py-0.1.0/src/computeragent/telemetry/event.py +355 -0
- computer_agent_py-0.1.0/src/computeragent/telemetry/middleware/__init__.py +8 -0
- computer_agent_py-0.1.0/src/computeragent/telemetry/middleware/guardrails.py +127 -0
- computer_agent_py-0.1.0/src/computeragent/telemetry/middleware/pii.py +158 -0
- computer_agent_py-0.1.0/src/computeragent/telemetry/pipeline.py +160 -0
- computer_agent_py-0.1.0/src/computeragent/telemetry/sinks/__init__.py +28 -0
- computer_agent_py-0.1.0/src/computeragent/telemetry/sinks/agentos.py +442 -0
- computer_agent_py-0.1.0/src/computeragent/telemetry/sinks/message_archive.py +136 -0
- computer_agent_py-0.1.0/src/computeragent/telemetry/sinks/otel.py +375 -0
- computer_agent_py-0.1.0/src/computeragent/types.py +13 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: computer-agent-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Drop-in replacement for claude-agent-sdk that adds a proxied telemetry pipeline (PII redaction + guardrails) with OpenTelemetry and AgentOS sinks.
|
|
5
|
+
Keywords: computeragent,claude-agent-sdk,claude,agent,telemetry,otel,opentelemetry,agentos,pii
|
|
6
|
+
Author: Abhi Bhat
|
|
7
|
+
Author-email: Abhi Bhat <abhishek.bhat@lyzr.ai>
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Dist: claude-agent-sdk>=0.2,<0.3
|
|
21
|
+
Requires-Dist: typing-extensions>=4.12
|
|
22
|
+
Requires-Dist: motor>=3.5 ; extra == 'agentos'
|
|
23
|
+
Requires-Dist: opentelemetry-api>=1.27 ; extra == 'all'
|
|
24
|
+
Requires-Dist: opentelemetry-sdk>=1.27 ; extra == 'all'
|
|
25
|
+
Requires-Dist: opentelemetry-exporter-otlp>=1.27 ; extra == 'all'
|
|
26
|
+
Requires-Dist: opentelemetry-semantic-conventions>=0.48b0 ; extra == 'all'
|
|
27
|
+
Requires-Dist: motor>=3.5 ; extra == 'all'
|
|
28
|
+
Requires-Dist: cedarpy>=4,<5 ; extra == 'all'
|
|
29
|
+
Requires-Dist: cedarpy>=4,<5 ; extra == 'cedar'
|
|
30
|
+
Requires-Dist: opentelemetry-api>=1.27 ; extra == 'otel'
|
|
31
|
+
Requires-Dist: opentelemetry-sdk>=1.27 ; extra == 'otel'
|
|
32
|
+
Requires-Dist: opentelemetry-exporter-otlp>=1.27 ; extra == 'otel'
|
|
33
|
+
Requires-Dist: opentelemetry-semantic-conventions>=0.48b0 ; extra == 'otel'
|
|
34
|
+
Requires-Python: >=3.10
|
|
35
|
+
Project-URL: Changelog, https://github.com/open-gitagent/computer-agent-py/blob/main/CHANGELOG.md
|
|
36
|
+
Project-URL: Homepage, https://github.com/open-gitagent/computer-agent-py
|
|
37
|
+
Project-URL: Issues, https://github.com/open-gitagent/computer-agent-py/issues
|
|
38
|
+
Provides-Extra: agentos
|
|
39
|
+
Provides-Extra: all
|
|
40
|
+
Provides-Extra: cedar
|
|
41
|
+
Provides-Extra: otel
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
|
|
44
|
+
# computer-agent-py
|
|
45
|
+
|
|
46
|
+
[](https://pypi.org/project/computer-agent-py/)
|
|
47
|
+
[](https://pypi.org/project/computer-agent-py/)
|
|
48
|
+
[](LICENSE)
|
|
49
|
+
|
|
50
|
+
**Drop-in replacement for [`claude-agent-sdk`](https://pypi.org/project/claude-agent-sdk/)** — change the import line, get a proxied telemetry pipeline with PII redaction, configurable OTel export, policy-based tool authorization, and full AgentOS integration for free.
|
|
51
|
+
|
|
52
|
+
```diff
|
|
53
|
+
- from claude_agent_sdk import ClaudeAgentOptions, query
|
|
54
|
+
- from claude_agent_sdk.types import ResultMessage
|
|
55
|
+
+ from computeragent import ClaudeAgentOptions, query
|
|
56
|
+
+ from computeragent.types import ResultMessage
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Every other line stays identical. `isinstance(msg, ResultMessage)` still works. The `claude` CLI subprocess, AWS Bedrock auth, MCP servers, permission modes, `cwd`, `add_dirs` — all behave exactly as the upstream SDK does.
|
|
60
|
+
|
|
61
|
+
> **Package vs import name** — PyPI distribution is `computer-agent-py` (hyphens for the wheel); the import name is `computeragent` (Python doesn't allow hyphens). So `pip install computer-agent-py` then `from computeragent import …`.
|
|
62
|
+
|
|
63
|
+
## Why use this
|
|
64
|
+
|
|
65
|
+
Adopting `computer-agent-py` in place of `claude-agent-sdk` gives you, without rewriting your agent code:
|
|
66
|
+
|
|
67
|
+
- **OpenTelemetry traces** following the GenAI Semantic Conventions, vendor-neutral (New Relic, Datadog, ClickHouse, Honeycomb, Tempo, Jaeger — one env-var change).
|
|
68
|
+
- **PII redaction** at the package boundary — email, phone, SSN, credit cards, AWS keys redacted before anything reaches a sink.
|
|
69
|
+
- **Generic guardrails** — attribute truncation, tool allowlists, per-session cost ceilings, content filters.
|
|
70
|
+
- **Policy-based tool-use authorization** — gate every tool call through OPA (remote) or Cedar (in-process). Fail-closed by default.
|
|
71
|
+
- **AgentOS visibility** — write to the same Mongo collections (`agent_registry`, `agent_logs`, `agent_messages`, `sessions`, `slack_threads`) the AgentOS frontend already reads. Library-mode agents show up in the Agents list, Logs tab, Chat transcript, and (with the harness running) live chat sandboxes.
|
|
72
|
+
- **Per-message archive** — every message, tool call, and policy decision archived to `agent_messages` for replay, RAG, and forensic audit.
|
|
73
|
+
|
|
74
|
+
## Install
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install computer-agent-py # core drop-in + OPA policy engine
|
|
78
|
+
pip install 'computer-agent-py[otel]' # + OpenTelemetry sink
|
|
79
|
+
pip install 'computer-agent-py[agentos]' # + AgentOS Mongo sinks
|
|
80
|
+
pip install 'computer-agent-py[cedar]' # + Cedar policy engine (in-process)
|
|
81
|
+
pip install 'computer-agent-py[all]' # everything
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Prerequisite** — same as upstream: the `claude` CLI binary on `PATH` and Anthropic / Bedrock credentials in the environment.
|
|
85
|
+
|
|
86
|
+
## Quickstart
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
import asyncio
|
|
90
|
+
from computeragent import ClaudeAgentOptions, query
|
|
91
|
+
from computeragent.types import ResultMessage
|
|
92
|
+
|
|
93
|
+
async def main():
|
|
94
|
+
options = ClaudeAgentOptions(
|
|
95
|
+
model="claude-sonnet-4-5",
|
|
96
|
+
system_prompt="You are a helpful assistant.",
|
|
97
|
+
allowed_tools=["Read", "Glob", "Grep"],
|
|
98
|
+
permission_mode="bypassPermissions",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
async for message in query(prompt="Summarize the README.md in this directory.", options=options):
|
|
102
|
+
if isinstance(message, ResultMessage):
|
|
103
|
+
print(f"answer ({message.num_turns} turns, ${message.total_cost_usd:.4f}):")
|
|
104
|
+
print(message.result)
|
|
105
|
+
|
|
106
|
+
asyncio.run(main())
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
That's it — same shape as `claude-agent-sdk`. Telemetry is configured from env vars; without anything set, nothing leaves your process.
|
|
110
|
+
|
|
111
|
+
## Configure telemetry
|
|
112
|
+
|
|
113
|
+
### Env-driven (zero code change)
|
|
114
|
+
|
|
115
|
+
| Variable | Effect |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Enables OTLP/HTTP export. Unset → console exporter (debug). |
|
|
118
|
+
| `OTEL_EXPORTER_OTLP_HEADERS` | Comma-separated `key=value` (e.g. `api-key=NRRX-...` for New Relic). |
|
|
119
|
+
| `OTEL_SERVICE_NAME` | `service.name` attribute on every span. Default: `computeragent`. |
|
|
120
|
+
| `COMPUTERAGENT_OTEL` | `disabled` to suppress OtelSink. |
|
|
121
|
+
| `AGENTOS_MONGO_URL` | When set + `[agentos]` installed, attaches AgentOS sinks (registry, logs, sessions, agent_messages, slack_threads). |
|
|
122
|
+
| `AGENTOS_MONGO_DB` | Mongo database name. Default: `agentos`. |
|
|
123
|
+
| `COMPUTERAGENT_CAPTURE_CONTENT` | `1` to include prompts/responses on OTel spans. Default: off. |
|
|
124
|
+
| `COMPUTERAGENT_CAPTURE_CONTENT_MODE` | `events` (default) \| `attributes` \| `both`. |
|
|
125
|
+
|
|
126
|
+
### Programmatic
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from computeragent import configure, PiiRedactor, GuardrailFilter
|
|
130
|
+
from computeragent.telemetry.sinks import OtelSink, AgentRegistrySink, MongoMessageSink
|
|
131
|
+
|
|
132
|
+
configure(
|
|
133
|
+
middleware=[
|
|
134
|
+
PiiRedactor(strategy="hash", extra_patterns=[r"BADGE-\d{6}"]),
|
|
135
|
+
GuardrailFilter(
|
|
136
|
+
max_attribute_length=4096,
|
|
137
|
+
tool_name_allowlist={"Read", "Glob", "Grep", "mcp__nordassist-tools__*"},
|
|
138
|
+
cost_ceiling_usd=1.50,
|
|
139
|
+
),
|
|
140
|
+
],
|
|
141
|
+
sinks=[
|
|
142
|
+
OtelSink(), # picks up env
|
|
143
|
+
AgentRegistrySink(mongo_url="mongodb://..."), # registry + logs + sessions + slack_threads
|
|
144
|
+
MongoMessageSink(mongo_url="mongodb://..."), # per-message archive
|
|
145
|
+
],
|
|
146
|
+
)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Vendor-neutral OTel destinations
|
|
150
|
+
|
|
151
|
+
The package emits standard OTLP — point it at any backend by setting two env vars. No code change.
|
|
152
|
+
|
|
153
|
+
| Destination | `OTEL_EXPORTER_OTLP_ENDPOINT` | `OTEL_EXPORTER_OTLP_HEADERS` |
|
|
154
|
+
|---|---|---|
|
|
155
|
+
| **New Relic** | `https://otlp.nr-data.net` | `api-key=<NR_LICENSE_KEY>` |
|
|
156
|
+
| **Datadog** (via DD Agent OTLP) | `http://localhost:4318` | _(unset; agent handles auth)_ |
|
|
157
|
+
| **Honeycomb** | `https://api.honeycomb.io` | `x-honeycomb-team=<KEY>` |
|
|
158
|
+
| **Grafana Cloud Tempo** | `https://tempo-prod-...grafana.net:443` | `authorization=Basic <base64>` |
|
|
159
|
+
| **Self-hosted Jaeger / Tempo / SigNoz** | `http://<host>:4318` | _(unset)_ |
|
|
160
|
+
| **Local console (debug)** | _(unset)_ | _(unset)_ |
|
|
161
|
+
|
|
162
|
+
Full recipe table — including direct New Relic / Datadog without an OTel collector — is in [`examples/e2e/destinations.md`](examples/e2e/destinations.md).
|
|
163
|
+
|
|
164
|
+
## Policy-based tool-use authorization
|
|
165
|
+
|
|
166
|
+
For agents that need stronger guardrails than `permission_mode`, attach an external policy engine. Activation is a single new option-field; the rest of the worker code is unchanged.
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from computeragent import ClaudeAgentOptions, PolicyPrincipal, PolicyResource, query
|
|
170
|
+
from computeragent.policy import OpaPolicyEngine, PolicyToolAuthorizer
|
|
171
|
+
|
|
172
|
+
opa = OpaPolicyEngine(
|
|
173
|
+
url="http://opa.platform:8181",
|
|
174
|
+
policy_path="computeragent/tools/allow",
|
|
175
|
+
fail_mode="deny", # default — engine errors deny the call
|
|
176
|
+
)
|
|
177
|
+
authorizer = PolicyToolAuthorizer(
|
|
178
|
+
engine=opa,
|
|
179
|
+
principal_resolver=lambda ctx: PolicyPrincipal(id="alice", groups=["engineer"]),
|
|
180
|
+
resource_resolver=lambda ctx: PolicyResource(agent_name="nordassist", model="claude-sonnet-4-5"),
|
|
181
|
+
context_resolver=lambda ctx: {"env": "prod"},
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
options = ClaudeAgentOptions(
|
|
185
|
+
...,
|
|
186
|
+
permission_mode="default", # was "bypassPermissions"
|
|
187
|
+
can_use_tool=authorizer,
|
|
188
|
+
)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Swap `OpaPolicyEngine` for `CedarPolicyEngine` (install with `pip install 'computer-agent-py[cedar]'`) and the worker code is identical:
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
from computeragent.policy import CedarPolicyEngine
|
|
195
|
+
|
|
196
|
+
cedar = CedarPolicyEngine(
|
|
197
|
+
policies=open("policies/computeragent.cedar").read(),
|
|
198
|
+
fail_mode="deny",
|
|
199
|
+
)
|
|
200
|
+
authorizer = PolicyToolAuthorizer(engine=cedar, principal_resolver=..., ...)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Sample policies are in [`examples/policies/`](examples/policies/) — one Rego file for OPA, one Cedar file. Each policy receives a canonical `PolicyInput` shape (`principal`, `action`, `resource`, `context`) so the engine choice is purely operational.
|
|
204
|
+
|
|
205
|
+
Every authorization decision emits a `policy_decision` telemetry event — `OtelSink` annotates the active `execute_tool` span with `policy.decision`, `policy.reason`, `policy.engine`, and `policy.latency_ms` so security audits and span queries co-locate.
|
|
206
|
+
|
|
207
|
+
## AgentOS integration
|
|
208
|
+
|
|
209
|
+
When `[agentos]` is installed and `AGENTOS_MONGO_URL` is set, every agent run writes to the Mongo collections the AgentOS frontend already reads:
|
|
210
|
+
|
|
211
|
+
| Collection | Per | What's in it |
|
|
212
|
+
|---|---|---|
|
|
213
|
+
| `agent_registry` | agent name | Identity + harness + model + last-seen; idempotent upsert |
|
|
214
|
+
| `agent_logs` | run | Rolled-up query/reply + tokens + cost + ok/error — drives the Logs tab |
|
|
215
|
+
| `sessions` | session | `entries[]` of `{type, text}` chat-bubble messages — drives the Chat tab transcript |
|
|
216
|
+
| `slack_threads` | session | TS parity row that drives the per-agent `sessionCount` + `lastActivity` aggregates |
|
|
217
|
+
| `agent_messages` | message | Per-event archive (`user_message`, `assistant_message`, `tool_use`, `tool_result`, `usage_snapshot`, `policy_decision`, `system_message`) for replay, RAG, audit |
|
|
218
|
+
|
|
219
|
+
The doc shapes are byte-for-byte compatible with the TypeScript `@open-gitagent/agent-registry-mongo` package — Python-driven agents show up in the same AgentOS UI that hosted TS agents do, with no frontend change.
|
|
220
|
+
|
|
221
|
+
### Live chat for library-mode agents
|
|
222
|
+
|
|
223
|
+
When the AgentRegistrySink writes its `agent_registry.source` row, it includes a full inline `IdentitySource` with `files: {agent.yaml, CLAUDE.md}` derived from your `ClaudeAgentOptions`. If a user clicks "New Chat" on the agent in the AgentOS SPA, the harness can clone those files into a sandbox workdir and spawn a live conversation — same UX as hosted (git-sourced) agents. The historical transcript stays available in the Chat tab regardless.
|
|
224
|
+
|
|
225
|
+
### DocumentDB compatibility
|
|
226
|
+
|
|
227
|
+
Prod deployments on AWS DocumentDB work without changes. The sinks use only operators DocumentDB supports — `$set`, `$setOnInsert`, `$push`, `update_one(upsert=True)`, `insert_one`. No aggregation pipelines, transactions, change streams, or TTL indexes. Set `MONGO_URL` with the standard `tls=true&tlsCAFile=...` params and mount the DocumentDB CA bundle.
|
|
228
|
+
|
|
229
|
+
## Architecture
|
|
230
|
+
|
|
231
|
+
```
|
|
232
|
+
user code: from computeragent import query, ClaudeAgentOptions
|
|
233
|
+
│
|
|
234
|
+
▼
|
|
235
|
+
computeragent._proxy.query ──┐
|
|
236
|
+
│ │
|
|
237
|
+
▼ │ PolicyToolAuthorizer
|
|
238
|
+
claude_agent_sdk → claude CLI subprocess → Bedrock│ (OPA / Cedar)
|
|
239
|
+
│ │ via can_use_tool
|
|
240
|
+
▼ │
|
|
241
|
+
yielded messages │
|
|
242
|
+
│ │
|
|
243
|
+
▼ │
|
|
244
|
+
TelemetryPipeline (taps stream) │
|
|
245
|
+
│ │
|
|
246
|
+
┌──── middleware ─────┐ │
|
|
247
|
+
│ PiiRedactor │ │
|
|
248
|
+
│ GuardrailFilter │ ◄────────────────────┘
|
|
249
|
+
│ <user-defined> │
|
|
250
|
+
└────────┬────────────┘
|
|
251
|
+
▼
|
|
252
|
+
┌──── fan-out to sinks ───────────────────────────┐
|
|
253
|
+
│ │
|
|
254
|
+
▼ ▼ ▼ ▼
|
|
255
|
+
OtelSink AgentRegistrySink MongoMessageSink <user>
|
|
256
|
+
│ │ │
|
|
257
|
+
▼ ▼ ▼
|
|
258
|
+
OTLP backend agent_registry agent_messages
|
|
259
|
+
(NR / DD / agent_logs (per-message
|
|
260
|
+
ClickHouse / sessions archive)
|
|
261
|
+
Tempo …) slack_threads
|
|
262
|
+
(drives AgentOS UI)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
The proxy is a pure tap — messages are never modified or reordered. Sinks run as background tasks so a slow exporter never stalls the agent hot path; `query()`'s `finally` block awaits them with a 5 s default timeout. Telemetry never breaks an agent run: middleware and sink exceptions are absorbed and logged.
|
|
266
|
+
|
|
267
|
+
## Live e2e against AgentOS
|
|
268
|
+
|
|
269
|
+
[`examples/e2e/`](examples/e2e/) contains a recipe for standing up the full TypeScript stack (mongo + clickhouse + otel-collector + harness + agentos-server + SPA) via docker-compose and running this package against it. After ~60s of warm-up plus a 30s Python script run, you'll see the agent appear in the SPA's Agents list with `logCount`, `sessionCount`, `lastActivity`, and `activeSandboxes` populated; the Logs tab will show the rollup; the Chat tab will show the per-message transcript; the Observability tab will show the OTel trace tree. See [`examples/e2e/README.md`](examples/e2e/README.md).
|
|
270
|
+
|
|
271
|
+
## Examples
|
|
272
|
+
|
|
273
|
+
| File | Demonstrates |
|
|
274
|
+
|---|---|
|
|
275
|
+
| [`examples/pdf_drop_in.py`](examples/pdf_drop_in.py) | The minimum drop-in change |
|
|
276
|
+
| [`examples/with_otel.py`](examples/with_otel.py) | OTel pointed at a local collector |
|
|
277
|
+
| [`examples/with_new_relic.py`](examples/with_new_relic.py) | OTel pointed at New Relic (just env vars) |
|
|
278
|
+
| [`examples/with_datadog.py`](examples/with_datadog.py) | OTel pointed at Datadog |
|
|
279
|
+
| [`examples/with_agentos.py`](examples/with_agentos.py) | AgentOS Mongo writes |
|
|
280
|
+
| [`examples/with_message_archive.py`](examples/with_message_archive.py) | Per-message archive |
|
|
281
|
+
| [`examples/with_pii_redaction.py`](examples/with_pii_redaction.py) | PII middleware in front of every sink |
|
|
282
|
+
| [`examples/with_opa_policy.py`](examples/with_opa_policy.py) | OPA-gated tool use |
|
|
283
|
+
| [`examples/with_cedar_policy.py`](examples/with_cedar_policy.py) | Cedar-gated tool use (in-process) |
|
|
284
|
+
| [`examples/multi_sink.py`](examples/multi_sink.py) | All sinks + all guardrails together |
|
|
285
|
+
| [`examples/e2e/run_live_demo.py`](examples/e2e/run_live_demo.py) | Full live demo against the AgentOS docker-compose stack |
|
|
286
|
+
|
|
287
|
+
## Upstream pin
|
|
288
|
+
|
|
289
|
+
This release tracks **`claude-agent-sdk` 0.2.x**. The pinned upstream version is recorded in [`CHANGELOG.md`](CHANGELOG.md). Bump deliberately — wire-protocol field additions in upstream get re-exported automatically (identity-preserving), but any behavioral changes need a passthrough audit.
|
|
290
|
+
|
|
291
|
+
## Development
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
git clone https://github.com/open-gitagent/computer-agent-py
|
|
295
|
+
cd computer-agent-py
|
|
296
|
+
uv sync --all-extras --dev
|
|
297
|
+
uv run ruff check src tests
|
|
298
|
+
uv run ruff format --check src tests
|
|
299
|
+
uv run mypy src
|
|
300
|
+
uv run pytest -q # 120+ unit tests
|
|
301
|
+
uv run pytest -q -m integration # requires ANTHROPIC_API_KEY + claude CLI
|
|
302
|
+
uv build
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## License
|
|
306
|
+
|
|
307
|
+
MIT — see [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# computer-agent-py
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/computer-agent-py/)
|
|
4
|
+
[](https://pypi.org/project/computer-agent-py/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
**Drop-in replacement for [`claude-agent-sdk`](https://pypi.org/project/claude-agent-sdk/)** — change the import line, get a proxied telemetry pipeline with PII redaction, configurable OTel export, policy-based tool authorization, and full AgentOS integration for free.
|
|
8
|
+
|
|
9
|
+
```diff
|
|
10
|
+
- from claude_agent_sdk import ClaudeAgentOptions, query
|
|
11
|
+
- from claude_agent_sdk.types import ResultMessage
|
|
12
|
+
+ from computeragent import ClaudeAgentOptions, query
|
|
13
|
+
+ from computeragent.types import ResultMessage
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Every other line stays identical. `isinstance(msg, ResultMessage)` still works. The `claude` CLI subprocess, AWS Bedrock auth, MCP servers, permission modes, `cwd`, `add_dirs` — all behave exactly as the upstream SDK does.
|
|
17
|
+
|
|
18
|
+
> **Package vs import name** — PyPI distribution is `computer-agent-py` (hyphens for the wheel); the import name is `computeragent` (Python doesn't allow hyphens). So `pip install computer-agent-py` then `from computeragent import …`.
|
|
19
|
+
|
|
20
|
+
## Why use this
|
|
21
|
+
|
|
22
|
+
Adopting `computer-agent-py` in place of `claude-agent-sdk` gives you, without rewriting your agent code:
|
|
23
|
+
|
|
24
|
+
- **OpenTelemetry traces** following the GenAI Semantic Conventions, vendor-neutral (New Relic, Datadog, ClickHouse, Honeycomb, Tempo, Jaeger — one env-var change).
|
|
25
|
+
- **PII redaction** at the package boundary — email, phone, SSN, credit cards, AWS keys redacted before anything reaches a sink.
|
|
26
|
+
- **Generic guardrails** — attribute truncation, tool allowlists, per-session cost ceilings, content filters.
|
|
27
|
+
- **Policy-based tool-use authorization** — gate every tool call through OPA (remote) or Cedar (in-process). Fail-closed by default.
|
|
28
|
+
- **AgentOS visibility** — write to the same Mongo collections (`agent_registry`, `agent_logs`, `agent_messages`, `sessions`, `slack_threads`) the AgentOS frontend already reads. Library-mode agents show up in the Agents list, Logs tab, Chat transcript, and (with the harness running) live chat sandboxes.
|
|
29
|
+
- **Per-message archive** — every message, tool call, and policy decision archived to `agent_messages` for replay, RAG, and forensic audit.
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install computer-agent-py # core drop-in + OPA policy engine
|
|
35
|
+
pip install 'computer-agent-py[otel]' # + OpenTelemetry sink
|
|
36
|
+
pip install 'computer-agent-py[agentos]' # + AgentOS Mongo sinks
|
|
37
|
+
pip install 'computer-agent-py[cedar]' # + Cedar policy engine (in-process)
|
|
38
|
+
pip install 'computer-agent-py[all]' # everything
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Prerequisite** — same as upstream: the `claude` CLI binary on `PATH` and Anthropic / Bedrock credentials in the environment.
|
|
42
|
+
|
|
43
|
+
## Quickstart
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import asyncio
|
|
47
|
+
from computeragent import ClaudeAgentOptions, query
|
|
48
|
+
from computeragent.types import ResultMessage
|
|
49
|
+
|
|
50
|
+
async def main():
|
|
51
|
+
options = ClaudeAgentOptions(
|
|
52
|
+
model="claude-sonnet-4-5",
|
|
53
|
+
system_prompt="You are a helpful assistant.",
|
|
54
|
+
allowed_tools=["Read", "Glob", "Grep"],
|
|
55
|
+
permission_mode="bypassPermissions",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
async for message in query(prompt="Summarize the README.md in this directory.", options=options):
|
|
59
|
+
if isinstance(message, ResultMessage):
|
|
60
|
+
print(f"answer ({message.num_turns} turns, ${message.total_cost_usd:.4f}):")
|
|
61
|
+
print(message.result)
|
|
62
|
+
|
|
63
|
+
asyncio.run(main())
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
That's it — same shape as `claude-agent-sdk`. Telemetry is configured from env vars; without anything set, nothing leaves your process.
|
|
67
|
+
|
|
68
|
+
## Configure telemetry
|
|
69
|
+
|
|
70
|
+
### Env-driven (zero code change)
|
|
71
|
+
|
|
72
|
+
| Variable | Effect |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Enables OTLP/HTTP export. Unset → console exporter (debug). |
|
|
75
|
+
| `OTEL_EXPORTER_OTLP_HEADERS` | Comma-separated `key=value` (e.g. `api-key=NRRX-...` for New Relic). |
|
|
76
|
+
| `OTEL_SERVICE_NAME` | `service.name` attribute on every span. Default: `computeragent`. |
|
|
77
|
+
| `COMPUTERAGENT_OTEL` | `disabled` to suppress OtelSink. |
|
|
78
|
+
| `AGENTOS_MONGO_URL` | When set + `[agentos]` installed, attaches AgentOS sinks (registry, logs, sessions, agent_messages, slack_threads). |
|
|
79
|
+
| `AGENTOS_MONGO_DB` | Mongo database name. Default: `agentos`. |
|
|
80
|
+
| `COMPUTERAGENT_CAPTURE_CONTENT` | `1` to include prompts/responses on OTel spans. Default: off. |
|
|
81
|
+
| `COMPUTERAGENT_CAPTURE_CONTENT_MODE` | `events` (default) \| `attributes` \| `both`. |
|
|
82
|
+
|
|
83
|
+
### Programmatic
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from computeragent import configure, PiiRedactor, GuardrailFilter
|
|
87
|
+
from computeragent.telemetry.sinks import OtelSink, AgentRegistrySink, MongoMessageSink
|
|
88
|
+
|
|
89
|
+
configure(
|
|
90
|
+
middleware=[
|
|
91
|
+
PiiRedactor(strategy="hash", extra_patterns=[r"BADGE-\d{6}"]),
|
|
92
|
+
GuardrailFilter(
|
|
93
|
+
max_attribute_length=4096,
|
|
94
|
+
tool_name_allowlist={"Read", "Glob", "Grep", "mcp__nordassist-tools__*"},
|
|
95
|
+
cost_ceiling_usd=1.50,
|
|
96
|
+
),
|
|
97
|
+
],
|
|
98
|
+
sinks=[
|
|
99
|
+
OtelSink(), # picks up env
|
|
100
|
+
AgentRegistrySink(mongo_url="mongodb://..."), # registry + logs + sessions + slack_threads
|
|
101
|
+
MongoMessageSink(mongo_url="mongodb://..."), # per-message archive
|
|
102
|
+
],
|
|
103
|
+
)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Vendor-neutral OTel destinations
|
|
107
|
+
|
|
108
|
+
The package emits standard OTLP — point it at any backend by setting two env vars. No code change.
|
|
109
|
+
|
|
110
|
+
| Destination | `OTEL_EXPORTER_OTLP_ENDPOINT` | `OTEL_EXPORTER_OTLP_HEADERS` |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| **New Relic** | `https://otlp.nr-data.net` | `api-key=<NR_LICENSE_KEY>` |
|
|
113
|
+
| **Datadog** (via DD Agent OTLP) | `http://localhost:4318` | _(unset; agent handles auth)_ |
|
|
114
|
+
| **Honeycomb** | `https://api.honeycomb.io` | `x-honeycomb-team=<KEY>` |
|
|
115
|
+
| **Grafana Cloud Tempo** | `https://tempo-prod-...grafana.net:443` | `authorization=Basic <base64>` |
|
|
116
|
+
| **Self-hosted Jaeger / Tempo / SigNoz** | `http://<host>:4318` | _(unset)_ |
|
|
117
|
+
| **Local console (debug)** | _(unset)_ | _(unset)_ |
|
|
118
|
+
|
|
119
|
+
Full recipe table — including direct New Relic / Datadog without an OTel collector — is in [`examples/e2e/destinations.md`](examples/e2e/destinations.md).
|
|
120
|
+
|
|
121
|
+
## Policy-based tool-use authorization
|
|
122
|
+
|
|
123
|
+
For agents that need stronger guardrails than `permission_mode`, attach an external policy engine. Activation is a single new option-field; the rest of the worker code is unchanged.
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from computeragent import ClaudeAgentOptions, PolicyPrincipal, PolicyResource, query
|
|
127
|
+
from computeragent.policy import OpaPolicyEngine, PolicyToolAuthorizer
|
|
128
|
+
|
|
129
|
+
opa = OpaPolicyEngine(
|
|
130
|
+
url="http://opa.platform:8181",
|
|
131
|
+
policy_path="computeragent/tools/allow",
|
|
132
|
+
fail_mode="deny", # default — engine errors deny the call
|
|
133
|
+
)
|
|
134
|
+
authorizer = PolicyToolAuthorizer(
|
|
135
|
+
engine=opa,
|
|
136
|
+
principal_resolver=lambda ctx: PolicyPrincipal(id="alice", groups=["engineer"]),
|
|
137
|
+
resource_resolver=lambda ctx: PolicyResource(agent_name="nordassist", model="claude-sonnet-4-5"),
|
|
138
|
+
context_resolver=lambda ctx: {"env": "prod"},
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
options = ClaudeAgentOptions(
|
|
142
|
+
...,
|
|
143
|
+
permission_mode="default", # was "bypassPermissions"
|
|
144
|
+
can_use_tool=authorizer,
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Swap `OpaPolicyEngine` for `CedarPolicyEngine` (install with `pip install 'computer-agent-py[cedar]'`) and the worker code is identical:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from computeragent.policy import CedarPolicyEngine
|
|
152
|
+
|
|
153
|
+
cedar = CedarPolicyEngine(
|
|
154
|
+
policies=open("policies/computeragent.cedar").read(),
|
|
155
|
+
fail_mode="deny",
|
|
156
|
+
)
|
|
157
|
+
authorizer = PolicyToolAuthorizer(engine=cedar, principal_resolver=..., ...)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Sample policies are in [`examples/policies/`](examples/policies/) — one Rego file for OPA, one Cedar file. Each policy receives a canonical `PolicyInput` shape (`principal`, `action`, `resource`, `context`) so the engine choice is purely operational.
|
|
161
|
+
|
|
162
|
+
Every authorization decision emits a `policy_decision` telemetry event — `OtelSink` annotates the active `execute_tool` span with `policy.decision`, `policy.reason`, `policy.engine`, and `policy.latency_ms` so security audits and span queries co-locate.
|
|
163
|
+
|
|
164
|
+
## AgentOS integration
|
|
165
|
+
|
|
166
|
+
When `[agentos]` is installed and `AGENTOS_MONGO_URL` is set, every agent run writes to the Mongo collections the AgentOS frontend already reads:
|
|
167
|
+
|
|
168
|
+
| Collection | Per | What's in it |
|
|
169
|
+
|---|---|---|
|
|
170
|
+
| `agent_registry` | agent name | Identity + harness + model + last-seen; idempotent upsert |
|
|
171
|
+
| `agent_logs` | run | Rolled-up query/reply + tokens + cost + ok/error — drives the Logs tab |
|
|
172
|
+
| `sessions` | session | `entries[]` of `{type, text}` chat-bubble messages — drives the Chat tab transcript |
|
|
173
|
+
| `slack_threads` | session | TS parity row that drives the per-agent `sessionCount` + `lastActivity` aggregates |
|
|
174
|
+
| `agent_messages` | message | Per-event archive (`user_message`, `assistant_message`, `tool_use`, `tool_result`, `usage_snapshot`, `policy_decision`, `system_message`) for replay, RAG, audit |
|
|
175
|
+
|
|
176
|
+
The doc shapes are byte-for-byte compatible with the TypeScript `@open-gitagent/agent-registry-mongo` package — Python-driven agents show up in the same AgentOS UI that hosted TS agents do, with no frontend change.
|
|
177
|
+
|
|
178
|
+
### Live chat for library-mode agents
|
|
179
|
+
|
|
180
|
+
When the AgentRegistrySink writes its `agent_registry.source` row, it includes a full inline `IdentitySource` with `files: {agent.yaml, CLAUDE.md}` derived from your `ClaudeAgentOptions`. If a user clicks "New Chat" on the agent in the AgentOS SPA, the harness can clone those files into a sandbox workdir and spawn a live conversation — same UX as hosted (git-sourced) agents. The historical transcript stays available in the Chat tab regardless.
|
|
181
|
+
|
|
182
|
+
### DocumentDB compatibility
|
|
183
|
+
|
|
184
|
+
Prod deployments on AWS DocumentDB work without changes. The sinks use only operators DocumentDB supports — `$set`, `$setOnInsert`, `$push`, `update_one(upsert=True)`, `insert_one`. No aggregation pipelines, transactions, change streams, or TTL indexes. Set `MONGO_URL` with the standard `tls=true&tlsCAFile=...` params and mount the DocumentDB CA bundle.
|
|
185
|
+
|
|
186
|
+
## Architecture
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
user code: from computeragent import query, ClaudeAgentOptions
|
|
190
|
+
│
|
|
191
|
+
▼
|
|
192
|
+
computeragent._proxy.query ──┐
|
|
193
|
+
│ │
|
|
194
|
+
▼ │ PolicyToolAuthorizer
|
|
195
|
+
claude_agent_sdk → claude CLI subprocess → Bedrock│ (OPA / Cedar)
|
|
196
|
+
│ │ via can_use_tool
|
|
197
|
+
▼ │
|
|
198
|
+
yielded messages │
|
|
199
|
+
│ │
|
|
200
|
+
▼ │
|
|
201
|
+
TelemetryPipeline (taps stream) │
|
|
202
|
+
│ │
|
|
203
|
+
┌──── middleware ─────┐ │
|
|
204
|
+
│ PiiRedactor │ │
|
|
205
|
+
│ GuardrailFilter │ ◄────────────────────┘
|
|
206
|
+
│ <user-defined> │
|
|
207
|
+
└────────┬────────────┘
|
|
208
|
+
▼
|
|
209
|
+
┌──── fan-out to sinks ───────────────────────────┐
|
|
210
|
+
│ │
|
|
211
|
+
▼ ▼ ▼ ▼
|
|
212
|
+
OtelSink AgentRegistrySink MongoMessageSink <user>
|
|
213
|
+
│ │ │
|
|
214
|
+
▼ ▼ ▼
|
|
215
|
+
OTLP backend agent_registry agent_messages
|
|
216
|
+
(NR / DD / agent_logs (per-message
|
|
217
|
+
ClickHouse / sessions archive)
|
|
218
|
+
Tempo …) slack_threads
|
|
219
|
+
(drives AgentOS UI)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The proxy is a pure tap — messages are never modified or reordered. Sinks run as background tasks so a slow exporter never stalls the agent hot path; `query()`'s `finally` block awaits them with a 5 s default timeout. Telemetry never breaks an agent run: middleware and sink exceptions are absorbed and logged.
|
|
223
|
+
|
|
224
|
+
## Live e2e against AgentOS
|
|
225
|
+
|
|
226
|
+
[`examples/e2e/`](examples/e2e/) contains a recipe for standing up the full TypeScript stack (mongo + clickhouse + otel-collector + harness + agentos-server + SPA) via docker-compose and running this package against it. After ~60s of warm-up plus a 30s Python script run, you'll see the agent appear in the SPA's Agents list with `logCount`, `sessionCount`, `lastActivity`, and `activeSandboxes` populated; the Logs tab will show the rollup; the Chat tab will show the per-message transcript; the Observability tab will show the OTel trace tree. See [`examples/e2e/README.md`](examples/e2e/README.md).
|
|
227
|
+
|
|
228
|
+
## Examples
|
|
229
|
+
|
|
230
|
+
| File | Demonstrates |
|
|
231
|
+
|---|---|
|
|
232
|
+
| [`examples/pdf_drop_in.py`](examples/pdf_drop_in.py) | The minimum drop-in change |
|
|
233
|
+
| [`examples/with_otel.py`](examples/with_otel.py) | OTel pointed at a local collector |
|
|
234
|
+
| [`examples/with_new_relic.py`](examples/with_new_relic.py) | OTel pointed at New Relic (just env vars) |
|
|
235
|
+
| [`examples/with_datadog.py`](examples/with_datadog.py) | OTel pointed at Datadog |
|
|
236
|
+
| [`examples/with_agentos.py`](examples/with_agentos.py) | AgentOS Mongo writes |
|
|
237
|
+
| [`examples/with_message_archive.py`](examples/with_message_archive.py) | Per-message archive |
|
|
238
|
+
| [`examples/with_pii_redaction.py`](examples/with_pii_redaction.py) | PII middleware in front of every sink |
|
|
239
|
+
| [`examples/with_opa_policy.py`](examples/with_opa_policy.py) | OPA-gated tool use |
|
|
240
|
+
| [`examples/with_cedar_policy.py`](examples/with_cedar_policy.py) | Cedar-gated tool use (in-process) |
|
|
241
|
+
| [`examples/multi_sink.py`](examples/multi_sink.py) | All sinks + all guardrails together |
|
|
242
|
+
| [`examples/e2e/run_live_demo.py`](examples/e2e/run_live_demo.py) | Full live demo against the AgentOS docker-compose stack |
|
|
243
|
+
|
|
244
|
+
## Upstream pin
|
|
245
|
+
|
|
246
|
+
This release tracks **`claude-agent-sdk` 0.2.x**. The pinned upstream version is recorded in [`CHANGELOG.md`](CHANGELOG.md). Bump deliberately — wire-protocol field additions in upstream get re-exported automatically (identity-preserving), but any behavioral changes need a passthrough audit.
|
|
247
|
+
|
|
248
|
+
## Development
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
git clone https://github.com/open-gitagent/computer-agent-py
|
|
252
|
+
cd computer-agent-py
|
|
253
|
+
uv sync --all-extras --dev
|
|
254
|
+
uv run ruff check src tests
|
|
255
|
+
uv run ruff format --check src tests
|
|
256
|
+
uv run mypy src
|
|
257
|
+
uv run pytest -q # 120+ unit tests
|
|
258
|
+
uv run pytest -q -m integration # requires ANTHROPIC_API_KEY + claude CLI
|
|
259
|
+
uv build
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## License
|
|
263
|
+
|
|
264
|
+
MIT — see [`LICENSE`](LICENSE).
|