You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 9ddc48d
Browse filesBrowse the repository at this point in the historyBrowse files
* python sdk
* redesign
* fix(e2e): update Part 2 to test offline mode instead of DaemonNotFoundError
Offline mode now activates when no daemon is running and no API key is
set — calls succeed with an audit log entry instead of raising
DaemonNotFoundError. Update the e2e test to assert the new behavior.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* adding feature to sdk
* fix: address code review issues — DLP, thread safety, API naming
- Resolve README.md merge conflict
- NODE9_SKIP=1 now emits a warning and writes an audit entry (no silent bypass)
- configure() is now thread-safe via threading.Lock
- DLP scan covers all string args, not just params named filename/content
- _dispatch() renamed to dispatch() — public API should not have underscore
(_dispatch kept as deprecated alias with DeprecationWarning)
- workspace validated at __init__ time with clear error if path doesn't exist
- @internal docstring clarifies it logs to stdout, not to audit trail
- README: shell=True examples now include sanitization warning
- README: _dispatch references updated to dispatch()
* fix: second round — DLP, Python 3.9 compat, run_id, API surface
- DLP now checks every string arg as a path candidate, not just params
named filename/path — fixes silent bypass on dest/target/output params
- tool() annotation uses Union[str, Callable] instead of str | Callable
— fixes TypeError on Python 3.9
- Docstring examples: shell=False + shlex.split(), write_code uses safe_path
- dispatch referenced consistently (not _dispatch) in all docstrings
- Node9Agent.new_session() added for server deployments with multiple users
- __all__ documents build_tools_anthropic/openai/dispatch/new_session
* adding feature to sdk
* adding feature to sdk
* Fix code review issues: safe_path keyword-only, NODE9_SKIP import warning, 13 new tests
- safe_path(filename, *, workspace=...) — workspace is now keyword-only to prevent
silent positional arg swaps; all call sites updated
- NODE9_SKIP=1 now warns at import time (not just per-call) so it's harder to
accidentally leave enabled in production
- new_session() docstring explicitly documents the one-instance-per-request requirement
- 13 new tests: dispatch() unknown tool (returns string, never raises), new_session()
UUID validity and concurrent calls, _build_tools() unannotated/int/bool/float params
and *args/**kwargs exclusion, safe_path symlink traversal rejection, configure()
called twice (second wins) and empty args don't overwrite
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix code review round 4: path safety for all args, README warnings, env var tests
- Path safety now checks ALL path-like args in @tool methods, not just the first;
a method with src+dest params now validates both (removed the break)
- dispatch() docstring clarifies the lookup is strictly registry-based (@tool marker)
- README run_shell example gets explicit warning that shlex.split still allows
arbitrary executables — callers must allowlist commands in production
- NODE9_SKIP env var table entry strengthened: "Never set in production"
- Git hooks now use the active virtualenv Python instead of hardcoded python3
- New tests: traversal in second path arg is caught, configure() wins over env var,
env var sets baseline before configure() is called
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* adding feature to sdk
* adding feature to sdk
* adding feature to sdk
* adding feature to sdk
* adding feature to sdk
* adding feature to sdk
* adding feature to sdk
* adding feature to sdk
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
`ActionDeniedException` has a `negotiation` property — a ready-made string you can feed back to the LLM so it can try a different approach instead of crashing:
99
-
100
-
```python
101
-
try:
102
-
delete_file("/etc/hosts")
103
-
except ActionDeniedException as e:
104
-
# e.negotiation = "Action 'delete_file' was blocked by Node9: Too dangerous. Choose a different approach."
105
-
response = llm.invoke(e.negotiation)
106
-
```
74
+
### Policy values
107
75
108
-
## Framework Examples
76
+
| Policy | Behaviour |
77
+
|---|---|
78
+
|`audit`| Log everything, auto-approve. Never blocks. Good for CI. |
79
+
|`require_approval`| Block + notify human. Good for production actions. |
80
+
|`block_on_rules`| Auto-block if rules match, audit otherwise. |
See [`examples/`](examples/) for full runnable examples.
115
+
See [`examples/`](examples/) for full runnable examples including AutoGen and LangGraph.
116
+
117
+
---
118
+
119
+
## Option 2 — `Node9Agent`: Build a governed agent from scratch
120
+
121
+
`Node9Agent` is a governance base class — DLP, path safety, audit, and tool dispatch built-in. It does **not** include an LLM loop; that is your framework's responsibility. This keeps the SDK framework-agnostic with zero dependencies.
`Node9Agent` introspects `@tool` methods and builds tool specs automatically — parameter names, types from annotations, and descriptions from docstrings. No manual schema writing.
199
+
200
+
---
201
+
202
+
## DLP and path safety as standalone utilities
203
+
204
+
```python
205
+
from node9 import dlp_scan, safe_path
206
+
207
+
# Scan content for secrets before writing to disk
208
+
hit = dlp_scan("output.txt", content)
209
+
if hit:
210
+
raiseValueError(f"Blocked: {hit}")
211
+
212
+
# Resolve a path safely within a workspace directory
# e.negotiation = "Action 'delete_file' was blocked by Node9: policy. Choose a different approach."
229
+
response = llm.invoke(e.negotiation)
230
+
```
231
+
232
+
---
233
+
234
+
## Environment variables
145
235
146
236
| Variable | Default | Description |
147
237
|---|---|---|
148
-
|`NODE9_DAEMON_PORT`|`7391`| Daemon port |
149
-
|`NODE9_AUTO_START`| — | Set to `1` to auto-launch the daemon if it's not running |
150
-
|`NODE9_SKIP`| — | Set to `1` to bypass all checks (unsafe — for tests only) |
238
+
|`NODE9_API_KEY`| — | Routes to node9 SaaS. Required for cloud / CI. |
239
+
|`NODE9_AGENT_NAME`| — | Agent identity — appears in audit logs and dashboard. |
240
+
|`NODE9_AGENT_POLICY`| — |`audit`, `require_approval`, or `block_on_rules`. |
241
+
|`NODE9_DAEMON_PORT`|`7391`| Local daemon port. |
242
+
|`NODE9_AUTO_START`| — | Set to `1` to auto-launch the local daemon if not running. |
243
+
|`NODE9_SKIP`| — | Set to `1` to bypass all checks. **Never set in production** — disables all governance. For unit tests only. If set, a warning is emitted at import time. |
244
+
245
+
## Development
246
+
247
+
After cloning, activate the git hooks (runs tests before every commit and push):
0 commit comments