diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05f9500..cdeb98b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: run: mypy src/ - name: Test (pytest) - run: python -m pytest -q --cov=agent_kernel --cov-report=term-missing + run: python -m pytest -q --cov=weaver_kernel --cov-report=term-missing - name: Examples run: | @@ -71,7 +71,7 @@ jobs: # weaver_contracts.conformance does not yet exist (dgenio/weaver-spec#4). # Replace this step with: # pip install weaver-contracts # PyPI dist name uses a hyphen - # python -m weaver_contracts.conformance --target agent_kernel + # python -m weaver_contracts.conformance --target weaver_kernel - name: weaver-spec conformance suite (stub) run: | echo "weaver-contracts 0.2.0 is on PyPI; weaver_contracts.conformance runner not yet available (dgenio/weaver-spec#4)." diff --git a/AGENTS.md b/AGENTS.md index ebffe45..3948e15 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,7 +7,7 @@ reference this file and add only tool-specific guidance. ## Repo layout ``` -src/agent_kernel/ — library source (one module per concern, ≤300 lines each) +src/weaver_kernel/ — library source (one module per concern, ≤300 lines each) drivers/ — capability drivers (one file per driver) firewall/ — context firewall (redaction, summarization, budgets) tests/ — pytest suite (one test file per module) @@ -57,7 +57,7 @@ Use these terms consistently. Never substitute synonyms: ## Security rules - Never log or print secret key material. -- HMAC secrets come from `AGENT_KERNEL_SECRET` env var; fallback to a random dev secret with a logged warning. +- HMAC secrets come from `WEAVER_KERNEL_SECRET` env var; fallback to a random dev secret with a logged warning. - Tokens are HMAC-signed but **not encrypted**. Never store secrets in token payloads. - Confused-deputy prevention: tokens bind `principal_id + capability_id + constraints`. - Never bypass token verification before capability invocation. @@ -69,7 +69,7 @@ See [docs/agent-context/invariants.md](docs/agent-context/invariants.md) for the ## Code conventions -**All modules (`src/agent_kernel/`):** +**All modules (`src/weaver_kernel/`):** Relative imports. Dataclasses with `slots=True`. Protocols for interfaces. `__all__` in every `__init__.py`. Google-style docstrings. `CamelCase` for classes, `snake_case` for functions. Error classes end with `Error`. @@ -100,7 +100,7 @@ See [docs/agent-context/workflows.md](docs/agent-context/workflows.md) for full ## Adding a capability driver -1. Implement the `Driver` protocol in `src/agent_kernel/drivers/`. +1. Implement the `Driver` protocol in `src/weaver_kernel/drivers/`. 2. Register it with `StaticRouter` or implement a custom `Router`. 3. Add integration tests in `tests/test_drivers.py`. diff --git a/CHANGELOG.md b/CHANGELOG.md index 634a326..a2862ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed +- **BREAKING — import renamed `agent_kernel` → `weaver_kernel` (#106).** The + package you `import` now matches the package you `pip install` + (`weaver-kernel`). Update imports from `agent_kernel...` to `weaver_kernel...`. + The `AGENT_KERNEL_SECRET` environment variable is likewise renamed to + `WEAVER_KERNEL_SECRET`. The OpenTelemetry span, metric, and attribute names + (`agent_kernel.*` → `weaver_kernel.*`) and the library's logger namespaces are + likewise renamed — update any dashboards, alerts, or log filters keyed on the + old names. No behavioral change beyond the rename. The GitHub + repository keeps its `agent-kernel` slug for now (GitHub redirects old URLs); + a settings rename to `weaver-kernel` is the optional final step. + ### Added +- README repositioned to lead with the unique **capability-token + tamper-evident + audit** value, with explicit boundary framing for the policy engine (vs + `AgentFence`, #111) and the context firewall (vs `contextweaver`, #110) so a + fresh reader can tell why `agent-kernel` exists alongside its siblings (#102). +- Standardized **"Part of the Weaver Stack"** README section with the shared + request-path diagram (contextweaver → ChainWeaver → agent-kernel → AgentFence) + and an explicit standalone-use / no-hard-sibling-dependency statement (#109). + *(Setting the `weaver-stack` GitHub topic is a repo-settings action outside + this PR.)* +- A prominent repo↔package↔import explainer at the install step in the README + (also the PyPI long description) plus a `## Naming` section in + [`docs/architecture.md`](docs/architecture.md) documenting the unification + decision; PyPI keywords now carry both `weaver-kernel` and `agent-kernel` + (#106). - Two more ecosystem integration cookbooks under `docs/integrations/`, each with a runnable, offline companion wired into `make ci`: - **ChainWeaver compiled flows as capabilities** (#95): a `ChainWeaverDriver` @@ -65,7 +91,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Capability marketplace, part 1 — manifest format & local registry: new `CapabilityDescriptor` and `CapabilityManifest` dataclasses (both JSON-round-trippable via `to_dict`/`from_dict`), new - `agent_kernel.federation` module with `build_manifest()`, + `weaver_kernel.federation` module with `build_manifest()`, `import_manifest()`, and `merge_sensitivity()`, and new `Kernel.advertise()` / `Kernel.import_remote()` methods. `Kernel` gained a `kernel_id` argument used as the manifest publisher identity. Three trust policies @@ -80,14 +106,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 protocol and a namespace section in [`docs/capabilities.md`](docs/capabilities.md). - Capability marketplace, part 2 — federated discovery: new - `agent_kernel.federation_discovery` module with `discover_peers()`, + `weaver_kernel.federation_discovery` module with `discover_peers()`, `sign_manifest()`, `verify_manifest()`, `serve_manifest_payload()`, and `DiscoveryRateLimiter`. `Kernel.discover_peers()` fetches one or more manifests over HTTP from peer URLs or a registry URL. Signed envelopes (HMAC-SHA256) detect tampering and let importers refuse unsigned manifests when a verification secret is in play (and vice versa). New errors `ManifestSignatureError` and `DiscoveryError`. (#51, closes #49) -- OpenTelemetry integration: new `agent_kernel.otel` module with +- OpenTelemetry integration: new `weaver_kernel.otel` module with `instrument_kernel(kernel)` that wraps `Kernel.invoke` and `Kernel.grant_capability` with OTel spans + metrics (invocation count, latency histogram, denial counter). No-op when the optional `[otel]` @@ -116,11 +142,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `RateLimiter` and rate-limit constants extracted from `policy.py` into a new `rate_limit.py` module; `policy.py` continues to re-export them under their original names. (#68) -- Tech debt: `kernel.py` split into the `agent_kernel.kernel` sub-package +- Tech debt: `kernel.py` split into the `weaver_kernel.kernel` sub-package to honour AGENTS.md's ≤ 300-line module bar. The `Kernel` class lives in `kernel/__init__.py`; heavy methods (invoke pipeline, dry-run, federation, streaming) delegate to sibling modules. Existing - `from agent_kernel import Kernel` / `from agent_kernel.kernel import Kernel` + `from weaver_kernel import Kernel` / `from weaver_kernel.kernel import Kernel` imports are unchanged. (#68) ### Documentation @@ -136,7 +162,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `docs/agent-context/architecture.md` from "`httpx` only" to "`httpx` + `pydantic`", pointing to `AGENTS.md` as the canonical dependency policy. (#90) -- The `agent_kernel` module docstring's `Errors::` block now lists every +- The `weaver_kernel` module docstring's `Errors::` block now lists every exported error class — added `TokenRevoked`, `AdapterParseError`, `CapabilityAlreadyRegistered`, `HandleConstraintViolation`, `ManifestSignatureError`, and `DiscoveryError`. (#91) @@ -145,7 +171,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added explicit dry-run regression tests for `HTTPDriver` and `MCPDriver`, pinning the kernel's driver-agnostic dry-run short-circuit. (#68) - `tests/test_public_api.py` — asserts every error class exported via `__all__` - appears in the `agent_kernel` module docstring, preventing public-API + appears in the `weaver_kernel` module docstring, preventing public-API docstring drift. (#91) - `tests/test_readme_quickstart.py` — extracts the README quickstart code block and executes it, asserting the documented output so the inline snippet cannot @@ -178,7 +204,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 auto-formatting. `AGENTS.md`, `docs/agent-context/workflows.md`, `docs/agent-context/review-checklist.md`, `CONTRIBUTING.md`, and the `README.md` development section are updated to describe the new chain. (#88) -- `agent_kernel.__version__` is now derived from the installed distribution +- `weaver_kernel.__version__` is now derived from the installed distribution metadata (`importlib.metadata.version("weaver-kernel")`) instead of a hand-maintained literal, so it can no longer drift from `pyproject.toml` (it previously reported `0.5.0` while the package shipped `0.7.0`/`0.8.0`). @@ -230,7 +256,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `HANDLE_PRINCIPAL_MISMATCH`, `MEMORY_WRITE_REQUIRES_WRITER`, `MEMORY_SENSITIVE_READ_DENIED`. - `HandleConstraintViolation` error class (subclass of `AgentKernelError`, - exported from `agent_kernel`) — carries an optional `reason_code` matching + exported from `weaver_kernel`) — carries an optional `reason_code` matching the `DenialReason` vocabulary so handle-side and grant-side denials share one set of stable codes. - `Kernel.expand` accepts an optional `principal: Principal` argument that @@ -277,8 +303,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 no raw argument values. `DryRunResult.policy_decision` also carries a synthesized single-step trace. (#73) - Stable machine-readable denial reason codes: new `DenialReason` and - `AllowReason` enums in `agent_kernel.policy_reasons` (also exported as - `from agent_kernel import DenialReason, AllowReason`). Every built-in + `AllowReason` enums in `weaver_kernel.policy_reasons` (also exported as + `from weaver_kernel import DenialReason, AllowReason`). Every built-in denial path on `DefaultPolicyEngine` and `DeclarativePolicyEngine` populates `PolicyDecision.reason_code`, `DenialExplanation.reason_code`, `FailedCondition.reason_code`, and `PolicyDenied.reason_code`. Tests should @@ -325,7 +351,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 per `AGENTS.md` ("never raise bare ValueError to callers"). - New public exports: `BudgetManager`, `BudgetExhausted`, `BudgetConfigError`, `TokenCounter`, `default_token_counter`, and `Kernel.budget` accessor property. -- LLM tool-format adapters and middleware (`agent_kernel.adapters`): `OpenAIMiddleware` (OpenAI +- LLM tool-format adapters and middleware (`weaver_kernel.adapters`): `OpenAIMiddleware` (OpenAI Responses API + Chat Completions, auto-detected on input) and `AnthropicMiddleware` (Anthropic Messages with `cache_control` support). Both translate `Capability` objects to vendor tool schemas, route tool calls through the full kernel pipeline (grant → invoke → firewall → trace), @@ -360,7 +386,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Kernel.explain_denial()` convenience method that calls the policy engine's `explain()` for a given `CapabilityRequest` and `Principal` without requiring a token. Raises `AgentKernelError` when the configured engine does not implement `explain()`. -- New public types exported from `agent_kernel`: `DeclarativePolicyEngine`, `ExplainingPolicyEngine`, +- New public types exported from `weaver_kernel`: `DeclarativePolicyEngine`, `ExplainingPolicyEngine`, `PolicyEngine`, `PolicyMatch`, `PolicyRule`, `DenialExplanation`, `FailedCondition`, `DryRunResult`, `PolicyConfigError`. - `policy` optional extra (`pip install weaver-kernel[policy]`) pulls in `pyyaml` and `tomli` (Python 3.10). @@ -368,14 +394,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Runtime dependencies now include `pydantic>=2` in addition to `httpx`. Pydantic is used by the new - `agent_kernel.adapters` package for JSON-Schema generation and argument validation when a + `weaver_kernel.adapters` package for JSON-Schema generation and argument validation when a `Capability` declares a `parameters_model`. Existing kernel behavior is unchanged; pydantic is not imported at module load by anything outside the adapters. - `PolicyEngine` protocol no longer requires `explain()`. Engines that need to support `Kernel.explain_denial()` should implement the new `ExplainingPolicyEngine` protocol. Built-in engines satisfy both. This avoids a breaking typing change for downstream implementers. - `DeclarativePolicyEngine` now defers `yaml` and `tomllib`/`tomli` imports into the corresponding - loaders, so `import agent_kernel` works without the `policy` extra installed. Calling + loaders, so `import weaver_kernel` works without the `policy` extra installed. Calling `from_yaml`/`from_toml` without the parser surfaces a `PolicyConfigError` with an install hint. - `Kernel.invoke(dry_run=True)` resolves `operation` the same way drivers do (`args.get("operation", capability_id)`) so `DryRunResult.operation` matches what a driver would diff --git a/Makefile b/Makefile index b014013..4550d82 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ type: python -m mypy src/ test: - python -m pytest -q --cov=agent_kernel + python -m pytest -q --cov=weaver_kernel example: python examples/basic_cli.py diff --git a/README.md b/README.md index 5f983b7..ae550e5 100644 --- a/README.md +++ b/README.md @@ -4,22 +4,32 @@ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/) [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) +**Least-privilege, revocable, principal-scoped authorization for agent tool calls — with a tamper-evident audit of everything that ran.** + A capability-based security kernel for AI agents operating in large tool ecosystems (MCP, A2A, 1000+ tools). +Every tool call gets a **capability token** (HMAC-signed, time-bounded, scoped to one principal and one capability) and a **tamper-evident audit trace** (`ActionTrace`) recording who invoked what, under which policy decision, with what result. That **authorization + audit** layer is `agent-kernel`'s unique contribution to the [Weaver stack](#part-of-the-weaver-stack) — neither `contextweaver` nor `AgentFence` provides it. + +### Why `agent-kernel` and not `contextweaver` or `AgentFence`? + +- **`contextweaver`** decides *what context the LLM sees*. **`agent-kernel`** decides *what the agent is allowed to run, and proves what it ran.* +- **`AgentFence`** is an external proxy that gates tool calls *at the process boundary*. **`agent-kernel`** is the *in-process* runtime that mints the capability token, enforces policy, firewalls the result, and writes the audit trace — compiled into your agent host. +- They compose: author policy once and enforce it both embedded (`agent-kernel`) and at the edge (`AgentFence`); produce a `Frame` in `agent-kernel` and let `contextweaver` do budgeted selection over it. See the boundary notes below. + ## 30-second pitch Modern AI agents face three hard problems when given access to hundreds or thousands of tools: -1. **Context blowup** — raw tool output floods the LLM context window. +1. **No authorization or audit** — nothing scopes what a tool call may do, and there's no record of what ran, when, and why. 2. **Tool-space interference** — agents accidentally invoke the wrong tool or escalate privileges. -3. **No audit trail** — there's no record of what ran, when, and why. +3. **Context blowup** — raw tool output floods the LLM context window. -`agent-kernel` solves all three with a thin, composable layer that sits above your tool execution layer: +`agent-kernel` solves all three with a thin, composable layer that sits above your tool execution layer. The first two features are its **unique, non-overlapping contribution**; the last two it *also* provides, with explicit boundaries against its siblings: -- **Capability Tokens** — HMAC-signed, time-bounded, principal-scoped. No token → no execution. -- **Policy Engine** — READ/WRITE/DESTRUCTIVE safety classes + PII/PCI sensitivity handling. -- **Context Firewall** — raw driver output is *never* returned to the LLM; always a bounded `Frame`. -- **Audit Trail** — every invocation creates an `ActionTrace` retrievable via `kernel.explain()`. +- **Capability Tokens** *(unique to agent-kernel)* — HMAC-signed, time-bounded, principal-scoped. No token → no execution. +- **Audit Trail** *(unique to agent-kernel)* — every invocation creates an `ActionTrace` retrievable via `kernel.explain()`. +- **Policy Engine** *(boundary vs AgentFence)* — READ/WRITE/DESTRUCTIVE safety classes + PII/PCI sensitivity handling, enforced **in-process**. `AgentFence` enforces an equivalent gate at the **external boundary**; the goal is to author one policy and enforce it both places (shared-policy contract — [#111](https://github.com/dgenio/agent-kernel/issues/111)). +- **Context Firewall** *(boundary vs contextweaver)* — raw driver output is *never* returned to the LLM; always a bounded `Frame`. `agent-kernel` is the **producer** of the canonical `Frame` at the execution boundary; `contextweaver` is a **consumer** that does budgeted selection over Frames — deliberate layering, not redundancy (canonical-`Frame` seam — [#110](https://github.com/dgenio/agent-kernel/issues/110)). ## Architecture @@ -35,25 +45,72 @@ graph LR K -->|record| AUD["Audit Trace"] ``` +## Part of the Weaver Stack + +`agent-kernel` is the **execution / authorization runtime** of the **Weaver +stack** — a set of composable, independently usable projects for building safe +LLM-agent systems. On the request path: + +``` +contextweaver ─► ChainWeaver ─► agent-kernel ─► AgentFence +(select & (deterministic (capability tokens, (external policy + compile context) tool chains) policy, firewall, gate at the edge) + tamper-evident audit) +``` + +| Project | Role in the stack | +|---|---| +| [contextweaver](https://github.com/dgenio/contextweaver) | Selects and compiles the context the LLM sees. | +| ChainWeaver | Orchestrates deterministic multi-step tool chains. | +| **agent-kernel** *(this repo)* | Authorizes, executes, firewalls, and audits tool calls in-process. | +| [AgentFence](https://github.com/dgenio/AgentFence) | Enforces a policy gate at the external process boundary. | +| [weaver-spec](https://github.com/dgenio/weaver-spec) | The shared contracts (invariants; capability/token/`Frame`/policy) the others conform to. | + +**Standalone by design.** `agent-kernel` has no hard dependency on any sibling +project — its only runtime dependencies are `httpx` and `pydantic`. Use it on +its own, or compose it with the rest of the stack; the siblings interoperate +through the shared [weaver-spec](https://github.com/dgenio/weaver-spec) +contracts, not through tight coupling. A deeper, per-project comparison — +including *when not* to reach for `agent-kernel` — is in +[How this relates to neighboring projects](#how-this-relates-to-neighboring-projects). + ## Quickstart ```bash pip install weaver-kernel ``` -> **Note:** The PyPI package is `weaver-kernel` (Weaver ecosystem), but the Python import remains `agent_kernel`. +```python +import weaver_kernel +``` + +> ### 📦 Repo ↔ package ↔ import — read this once +> +> | Where you see it | Name | +> |---|---| +> | GitHub repository | `dgenio/agent-kernel` | +> | PyPI — what you `pip install` | **`weaver-kernel`** | +> | Python — what you `import` | **`weaver_kernel`** | +> +> **Decision (2026-06):** the install name and the import name are unified on +> **`weaver-kernel` / `weaver_kernel`** — the two names you actually type. There +> is **no `agent_kernel` import any more**; use `weaver_kernel`. The GitHub repo +> keeps its historical `agent-kernel` slug for now (GitHub redirects old URLs); +> the package is part of the [**Weaver stack**](#part-of-the-weaver-stack), which +> is why the distribution is `weaver-`prefixed. See +> [docs/architecture.md](docs/architecture.md#naming) for the full rationale. > **New here?** [docs/tutorial.md](docs/tutorial.md) walks through register → grant → invoke → expand → explain in five minutes. ```python import asyncio, os -os.environ["AGENT_KERNEL_SECRET"] = "my-secret" +os.environ["WEAVER_KERNEL_SECRET"] = "my-secret" -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, InMemoryDriver, Kernel, Principal, SafetyClass, StaticRouter, ) -from agent_kernel.models import CapabilityRequest +from weaver_kernel.models import CapabilityRequest # 1. Register a capability registry = CapabilityRegistry() @@ -178,7 +235,7 @@ See [docs/agent-context/invariants.md](docs/agent-context/invariants.md) for the > **v0.1 is not production-hardened for real authentication.** - HMAC tokens are tamper-evident (SHA-256) but **not encrypted**. Do not put sensitive data in token fields. -- Set `AGENT_KERNEL_SECRET` to a strong random value in production. If unset, a random dev secret is generated per-process with a warning. +- Set `WEAVER_KERNEL_SECRET` to a strong random value in production. If unset, a random dev secret is generated per-process with a warning. - PII redaction is heuristic (regex). It is not a substitute for proper data governance. - See [docs/security.md](docs/security.md) for the full threat model. diff --git a/docs/agent-context/invariants.md b/docs/agent-context/invariants.md index b590d60..cb4435c 100644 --- a/docs/agent-context/invariants.md +++ b/docs/agent-context/invariants.md @@ -36,7 +36,7 @@ These constraints are non-negotiable. Violating any one silently degrades securi 4. **Never store secrets in token payloads.** Tokens are HMAC-signed but not encrypted. Payload contents are readable by anyone who holds the token. -5. **Never log or print secret key material.** The `AGENT_KERNEL_SECRET` and any +5. **Never log or print secret key material.** The `WEAVER_KERNEL_SECRET` and any derived keys must stay out of logs, error messages, and traces. 6. **Never add dependencies without justification.** The dependency list is intentionally diff --git a/docs/agent-context/review-checklist.md b/docs/agent-context/review-checklist.md index fcb5996..bbe2d2b 100644 --- a/docs/agent-context/review-checklist.md +++ b/docs/agent-context/review-checklist.md @@ -51,7 +51,7 @@ When a change touches one of these areas, verify the related files stay aligned: | Policy rules or `SensitivityTag` | `docs/capabilities.md`, `AGENTS.md` security rules | | Firewall behavior | `docs/context_firewall.md`, `AGENTS.md` security rules | | Driver Protocol | `docs/integrations.md`, `AGENTS.md` driver how-to | -| `pyproject.toml` version | `src/agent_kernel/__init__.py` `__version__` | +| `pyproject.toml` version | `src/weaver_kernel/__init__.py` `__version__` | | Dependency list | `AGENTS.md` (justification required) | | Error classes | `errors.py` naming convention (must end with `Error`) | | Review checklist items | `.github/copilot-instructions.md` review checklist | diff --git a/docs/architecture.md b/docs/architecture.md index 289e0ff..9e2fc9c 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,5 +1,25 @@ # Architecture +## Naming + +This project carries three related names; they are deliberately reconciled: + +| Where you see it | Name | +|---|---| +| GitHub repository | `dgenio/agent-kernel` | +| PyPI distribution (`pip install`) | `weaver-kernel` | +| Python import | `weaver_kernel` | + +**Decision (2026-06):** the install name and the import name are unified on +`weaver-kernel` / `weaver_kernel` — the two strings a user actually types — so +`pip install weaver-kernel` is followed by `import weaver_kernel` with no +mismatch. There is no `agent_kernel` import any more. The `weaver-` prefix marks +membership in the [Weaver stack](../README.md#part-of-the-weaver-stack); the +GitHub repository keeps its historical `agent-kernel` slug (GitHub redirects old +URLs), which is the only remaining surface where the legacy name appears. A +repository-slug rename to `weaver-kernel` is the optional final step and can be +done in repo settings without code changes. + ## Overview `agent-kernel` is a capability-based security kernel that sits **above** raw tool execution (MCP, HTTP APIs, internal services) and **below** the LLM context window. @@ -55,7 +75,7 @@ Both built-in engines satisfy `ExplainingPolicyEngine`: 6. **MEMORY** — `memory.read` with `scope.memory_scope == "sensitive"` requires role `memory_reader_sensitive|admin`; `memory.write` / DESTRUCTIVE memory requires role `memory_writer|admin`. Project-scoped memory reads are allowed by default. The kernel also redacts `payload`/`content`/`value`/`memory`/`text`/`body` keys from `ActionTrace.args` for any capability whose ID starts with `memory.` 7. **max_rows** — 50 (user), 500 (service) 8. **Rate limiting** — sliding-window per `(principal_id, capability_id)` (60 READ / 10 WRITE / 2 DESTRUCTIVE per 60s; service role gets 10×) -- **`DeclarativePolicyEngine`** — loads rules from a YAML or TOML file (or a plain dict). Supports `safety_class`, `sensitivity`, `roles`, `attributes`, `min_justification`, `intent`, and `scope` match conditions; `allow`/`deny` actions; per-rule `constraints` merged into the resulting `PolicyDecision`; configurable `default` action. Rules are evaluated top-down with first-match-wins. `pyyaml` and `tomli` are optional dependencies — `import agent_kernel` works without them; calling `from_yaml`/`from_toml` without the parser raises `PolicyConfigError` with an install hint. +- **`DeclarativePolicyEngine`** — loads rules from a YAML or TOML file (or a plain dict). Supports `safety_class`, `sensitivity`, `roles`, `attributes`, `min_justification`, `intent`, and `scope` match conditions; `allow`/`deny` actions; per-rule `constraints` merged into the resulting `PolicyDecision`; configurable `default` action. Rules are evaluated top-down with first-match-wins. `pyyaml` and `tomli` are optional dependencies — `import weaver_kernel` works without them; calling `from_yaml`/`from_toml` without the parser raises `PolicyConfigError` with an install hint. #### Intent and scope on requests @@ -136,7 +156,7 @@ Stores full results by opaque handle ID with TTL. `expand()` supports pagination ### TraceStore Records every `ActionTrace`. `explain(action_id)` returns the full audit record. On a successful invocation the trace also carries a `result_summary` — a redaction-safe dict of counts/flags (`fact_count`, `row_count`, `warning_count`, `has_handle`) derived from the firewalled `Frame`, never from raw driver data — so an invocation's outcome is auditable directly (e.g. a repository safety check passed iff `result_summary["row_count"] == 0`). Failed runs have `result_summary == None`. -### Adapters (`agent_kernel.adapters`) +### Adapters (`weaver_kernel.adapters`) Vendor-specific tool-format adapters that translate between `Capability` objects and the tool shapes used by LLM provider APIs: diff --git a/docs/capabilities.md b/docs/capabilities.md index 65aeafc..ed38bc4 100644 --- a/docs/capabilities.md +++ b/docs/capabilities.md @@ -164,7 +164,7 @@ applies (`"deny"` unless overridden). ```python from pathlib import Path -from agent_kernel import DeclarativePolicyEngine, Kernel +from weaver_kernel import DeclarativePolicyEngine, Kernel # YAML or TOML — both formats are interchangeable. policy = DeclarativePolicyEngine.from_yaml(Path("examples/policies/default.yaml")) @@ -195,7 +195,7 @@ rule requiring the attribute paired with `default: deny`. See worked example. `pyyaml` and `tomli` are **optional** — they live behind the `[policy]` -extra. `import agent_kernel` always works; calling `from_yaml` / `from_toml` +extra. `import weaver_kernel` always works; calling `from_yaml` / `from_toml` without the parser installed raises `PolicyConfigError` with an install hint. ## Denial explanations diff --git a/docs/context_firewall.md b/docs/context_firewall.md index 9dec46b..a7faa5f 100644 --- a/docs/context_firewall.md +++ b/docs/context_firewall.md @@ -9,7 +9,7 @@ transforms every `RawResult` into a bounded `Frame` before the LLM sees it. ## Budgets ```python -from agent_kernel.firewall.budgets import Budgets +from weaver_kernel.firewall.budgets import Budgets Budgets( max_rows=50, # max rows in table_preview @@ -76,7 +76,7 @@ session. It is optional — if you don't attach one, kernel behavior is unchanged. ```python -from agent_kernel import BudgetManager, Kernel +from weaver_kernel import BudgetManager, Kernel manager = BudgetManager(total_budget=100_000) kernel = Kernel(registry, budget_manager=manager) @@ -133,7 +133,7 @@ caps apply on every yielded Frame — secrets cannot leak just because they arrived in chunk N rather than the final aggregate. ```python -from agent_kernel.drivers.base import ExecutionContext, StreamingDriver +from weaver_kernel.drivers.base import ExecutionContext, StreamingDriver class MyStreamingDriver: driver_id = "stream" @@ -164,18 +164,18 @@ one `ActionTrace` covering the whole stream. ## Observability -`agent_kernel.instrument_kernel(kernel)` installs OpenTelemetry spans and +`weaver_kernel.instrument_kernel(kernel)` installs OpenTelemetry spans and metric emission on `Kernel.invoke` and `Kernel.grant_capability`: ```python -from agent_kernel import Kernel, instrument_kernel, OTEL_AVAILABLE +from weaver_kernel import Kernel, instrument_kernel, OTEL_AVAILABLE kernel = Kernel(registry=...) if OTEL_AVAILABLE: instrument_kernel(kernel) # no-op when [otel] extra not installed ``` -Spans: `agent_kernel.invoke`, `agent_kernel.grant`. Metrics: -`agent_kernel.invocations` (counter), `agent_kernel.invocation_duration` -(histogram, ms), `agent_kernel.policy_denials` (counter). The call is +Spans: `weaver_kernel.invoke`, `weaver_kernel.grant`. Metrics: +`weaver_kernel.invocations` (counter), `weaver_kernel.invocation_duration` +(histogram, ms), `weaver_kernel.policy_denials` (counter). The call is idempotent — repeat invocations on the same kernel are no-ops. diff --git a/docs/federation.md b/docs/federation.md index 64f1b96..22b5b36 100644 --- a/docs/federation.md +++ b/docs/federation.md @@ -14,7 +14,7 @@ A single kernel can: 1. **Advertise** its capabilities as a JSON-serialisable - [`CapabilityManifest`](../src/agent_kernel/models.py). + [`CapabilityManifest`](../src/weaver_kernel/models.py). 2. **Import** another kernel's manifest, registering each capability locally and routing invocations through a caller-supplied driver (typically [`HTTPDriver`](integrations.md) or @@ -34,7 +34,7 @@ imported capabilities: ## Publishing a manifest ```python -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, HMACTokenProvider, Kernel, SafetyClass, SensitivityTag, ) @@ -76,7 +76,7 @@ print(manifest.to_dict()) The manifest deliberately omits internal driver IDs, operation names, `parameters_model` Python references, and `tool_hints`. Only the -[`CapabilityDescriptor`](../src/agent_kernel/models.py) projection of each +[`CapabilityDescriptor`](../src/weaver_kernel/models.py) projection of each capability is published. ## Importing a manifest @@ -85,10 +85,10 @@ capability is published. import json import httpx -from agent_kernel import ( +from weaver_kernel import ( CapabilityManifest, CapabilityRegistry, HMACTokenProvider, Kernel, ) -from agent_kernel.drivers.http import HTTPDriver, HTTPEndpoint +from weaver_kernel.drivers.http import HTTPDriver, HTTPEndpoint # 1. Fetch the manifest by whatever transport suits you. raw = httpx.get("https://agent-b.example/kernel/manifest").json() @@ -145,11 +145,11 @@ their own capability records and want the canonical strictest-wins union. ## Reference -- Models: [`CapabilityDescriptor`](../src/agent_kernel/models.py), - [`CapabilityManifest`](../src/agent_kernel/models.py). -- Functions: [`build_manifest`](../src/agent_kernel/federation.py), - [`import_manifest`](../src/agent_kernel/federation.py), - [`merge_sensitivity`](../src/agent_kernel/federation.py). +- Models: [`CapabilityDescriptor`](../src/weaver_kernel/models.py), + [`CapabilityManifest`](../src/weaver_kernel/models.py). +- Functions: [`build_manifest`](../src/weaver_kernel/federation.py), + [`import_manifest`](../src/weaver_kernel/federation.py), + [`merge_sensitivity`](../src/weaver_kernel/federation.py). - Kernel methods: `Kernel.advertise()`, `Kernel.import_remote()`, `Kernel.kernel_id`. - Errors: `FederationError`, `ManifestError`, `TrustPolicyError`. @@ -169,7 +169,7 @@ The discovery layer on top of the local marketplace adds two pieces: registry URL that returns a JSON list of peer URLs. ```python -from agent_kernel import discover_peers, sign_manifest, serve_manifest_payload +from weaver_kernel import discover_peers, sign_manifest, serve_manifest_payload # Publisher side — expose the manifest from any ASGI framework. @app.get("/kernel/manifest") @@ -210,9 +210,9 @@ authority. ### Reference -- Functions: [`discover_peers`](../src/agent_kernel/federation_discovery.py), - [`sign_manifest`](../src/agent_kernel/federation_discovery.py), - [`verify_manifest`](../src/agent_kernel/federation_discovery.py), - [`serve_manifest_payload`](../src/agent_kernel/federation_discovery.py). +- Functions: [`discover_peers`](../src/weaver_kernel/federation_discovery.py), + [`sign_manifest`](../src/weaver_kernel/federation_discovery.py), + [`verify_manifest`](../src/weaver_kernel/federation_discovery.py), + [`serve_manifest_payload`](../src/weaver_kernel/federation_discovery.py). - Kernel methods: `Kernel.discover_peers()`. - New errors: `ManifestSignatureError`, `DiscoveryError`. diff --git a/docs/integrations.md b/docs/integrations.md index a80f5af..d02b4c9 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -15,8 +15,8 @@ pip install "weaver-kernel[mcp]" ```python import asyncio -from agent_kernel import CapabilityRegistry, Kernel, StaticRouter -from agent_kernel.drivers.mcp import MCPDriver +from weaver_kernel import CapabilityRegistry, Kernel, StaticRouter +from weaver_kernel.drivers.mcp import MCPDriver async def main() -> None: @@ -49,8 +49,8 @@ asyncio.run(main()) ```python import asyncio -from agent_kernel import CapabilityRegistry, Kernel, StaticRouter -from agent_kernel.drivers.mcp import MCPDriver +from weaver_kernel import CapabilityRegistry, Kernel, StaticRouter +from weaver_kernel.drivers.mcp import MCPDriver async def main() -> None: @@ -95,7 +95,7 @@ asyncio.run(main()) The built-in `HTTPDriver` supports GET, POST, PUT, DELETE: ```python -from agent_kernel.drivers.http import HTTPDriver, HTTPEndpoint +from weaver_kernel.drivers.http import HTTPDriver, HTTPEndpoint driver = HTTPDriver(driver_id="my_api") driver.register_endpoint("users.list", HTTPEndpoint( @@ -131,7 +131,7 @@ When mapping MCP tools to capabilities, prefer task-shaped names: ## LLM tool-format adapters -`agent_kernel.adapters` converts `Capability` objects into the tool shapes +`weaver_kernel.adapters` converts `Capability` objects into the tool shapes expected by OpenAI and Anthropic, and routes the matching tool-call objects back through the kernel pipeline (grant → invoke → firewall → trace). The adapters are pure dict transforms — there is **no runtime dependency** on the @@ -154,7 +154,7 @@ firewall — it is *not* used as an input schema source. ```python from pydantic import BaseModel, Field -from agent_kernel import Capability, SafetyClass +from weaver_kernel import Capability, SafetyClass class ListInvoicesArgs(BaseModel): @@ -176,7 +176,7 @@ list_invoices = Capability( ```python import asyncio -from agent_kernel import Kernel, OpenAIMiddleware, Principal +from weaver_kernel import Kernel, OpenAIMiddleware, Principal async def main() -> None: @@ -259,7 +259,7 @@ accepts `null` as a valid value for such fields, so the LLM can effectively ```python import asyncio -from agent_kernel import AnthropicMiddleware, Kernel, Principal +from weaver_kernel import AnthropicMiddleware, Kernel, Principal async def main() -> None: @@ -330,7 +330,7 @@ exception. ## OpenTelemetry -`agent_kernel.instrument_kernel(kernel)` wraps `Kernel.invoke` and +`weaver_kernel.instrument_kernel(kernel)` wraps `Kernel.invoke` and `Kernel.grant_capability` with OTel spans + metrics. The optional `[otel]` extra brings in `opentelemetry-api`; everything is a strict no-op when the extra is not installed. @@ -342,7 +342,7 @@ pip install 'weaver-kernel[otel]' opentelemetry-sdk \ ``` ```python -from agent_kernel import Kernel, instrument_kernel +from weaver_kernel import Kernel, instrument_kernel kernel = Kernel(registry=...) instrument_kernel(kernel) @@ -353,14 +353,14 @@ instrument_kernel(kernel) | Telemetry | Name | Notes | |-----------|------|-------| -| Span | `agent_kernel.invoke` | attrs: `principal_id`, `capability_id`, `response_mode`, `dry_run` | -| Span | `agent_kernel.grant` | attrs: `principal_id`, `capability_id` | -| Counter | `agent_kernel.invocations` | labels: `capability_id`, `status` (`success`/`error`) | -| Histogram | `agent_kernel.invocation_duration` | unit: ms | -| Counter | `agent_kernel.policy_denials` | labels: `capability_id`, `reason_code` | +| Span | `weaver_kernel.invoke` | attrs: `principal_id`, `capability_id`, `response_mode`, `dry_run` | +| Span | `weaver_kernel.grant` | attrs: `principal_id`, `capability_id` | +| Counter | `weaver_kernel.invocations` | labels: `capability_id`, `status` (`success`/`error`) | +| Histogram | `weaver_kernel.invocation_duration` | unit: ms | +| Counter | `weaver_kernel.policy_denials` | labels: `capability_id`, `reason_code` | `instrument_kernel` is idempotent — calling twice on the same kernel is a -no-op. Use `agent_kernel.otel.reset_instrumentation(kernel)` in tests to +no-op. Use `weaver_kernel.otel.reset_instrumentation(kernel)` in tests to re-instrument with a different provider. ## Ecosystem integration patterns diff --git a/docs/security.md b/docs/security.md index dea2ae8..4bbe95f 100644 --- a/docs/security.md +++ b/docs/security.md @@ -86,7 +86,7 @@ preserved so audit can still confirm an action took place. > **v0.1 is not production-hardened for real authentication.** - HMAC tokens are tamper-evident but **not encrypted**. Do not put sensitive data in token fields. -- The `AGENT_KERNEL_SECRET` must be kept secret. Rotate it if compromised. +- The `WEAVER_KERNEL_SECRET` must be kept secret. Rotate it if compromised. - The default `InMemoryDriver` has no persistence — suitable for testing only. - PII redaction is heuristic (regex-based). It is not a substitute for proper data governance. - Rate limiting is enforced per `(principal_id, capability_id)` pair using a sliding window. diff --git a/docs/tutorial.md b/docs/tutorial.md index 6e1500c..9ddbc45 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -6,7 +6,7 @@ is copy-pasteable; the runnable companion is [`examples/tutorial.py`](../examples/tutorial.py) (covered by CI). > The PyPI package is **`weaver-kernel`** but the Python import is -> **`agent_kernel`**. We use both names in this document. +> **`weaver_kernel`**. We use both names in this document. ## What you'll learn @@ -44,7 +44,7 @@ reproducible: ```python import os -os.environ["AGENT_KERNEL_SECRET"] = "tutorial-secret-do-not-use-in-prod" +os.environ["WEAVER_KERNEL_SECRET"] = "tutorial-secret-do-not-use-in-prod" ``` ## 1. Register a capability @@ -55,7 +55,7 @@ firewall how to treat the data. `allowed_fields` is the projection the firewall applies before any row reaches the LLM. ```python -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, ImplementationRef, @@ -87,8 +87,8 @@ registry.register( `HTTPDriver` or `MCPDriver` — see step 8. ```python -from agent_kernel import HMACTokenProvider, InMemoryDriver, Kernel, StaticRouter -from agent_kernel.drivers.base import ExecutionContext +from weaver_kernel import HMACTokenProvider, InMemoryDriver, Kernel, StaticRouter +from weaver_kernel.drivers.base import ExecutionContext INVOICES = [ {"id": "INV-001", "customer_name": "Alice", "email": "alice@example.com", "amount": 120.0, "status": "paid"}, @@ -114,7 +114,7 @@ for any PII-tagged capability. Without it, the grant is denied with `reason_code="missing_tenant_attribute"`. ```python -from agent_kernel import Principal +from weaver_kernel import Principal alice = Principal(principal_id="alice", roles=["reader"], attributes={"tenant": "acme"}) ``` @@ -125,7 +125,7 @@ alice = Principal(principal_id="alice", roles=["reader"], attributes={"tenant": `CapabilityToken`. No token, no invocation. ```python -from agent_kernel.models import CapabilityRequest +from weaver_kernel.models import CapabilityRequest request = CapabilityRequest(capability_id="billing.invoices.list", goal="list recent invoices") token = kernel.get_token(request, alice, justification="") @@ -198,7 +198,7 @@ print(expanded.table_preview) Asking for a disallowed field is rejected with a stable `reason_code`: ```python -from agent_kernel.errors import HandleConstraintViolation +from weaver_kernel.errors import HandleConstraintViolation try: kernel.expand(handle_frame.handle, query={"fields": ["email"]}, principal=alice) @@ -218,7 +218,7 @@ denial carries both a human-readable `reason` and a stable `reason_code` your code can branch on. ```python -from agent_kernel.errors import PolicyDenied +from weaver_kernel.errors import PolicyDenied registry.register( Capability( @@ -242,7 +242,7 @@ except PolicyDenied as exc: print(str(exc)) # "WRITE capabilities require the 'writer' or 'admin' role..." ``` -Stable reason codes come from `agent_kernel.policy_reasons.DenialReason`. +Stable reason codes come from `weaver_kernel.policy_reasons.DenialReason`. Tests should assert on the code, not on the human-readable string. ## 8. Audit with `explain()` @@ -265,7 +265,7 @@ firewall behave identically. To talk to a real MCP server, replace Streamable HTTP, live in [`docs/integrations.md`](integrations.md)): ```python -from agent_kernel.drivers.mcp import MCPDriver +from weaver_kernel.drivers.mcp import MCPDriver driver = MCPDriver.from_stdio( command="python", diff --git a/examples/basic_cli.py b/examples/basic_cli.py index efad0ce..296118c 100644 --- a/examples/basic_cli.py +++ b/examples/basic_cli.py @@ -9,9 +9,9 @@ import os # Use a stable test secret so the example is reproducible. -os.environ.setdefault("AGENT_KERNEL_SECRET", "example-secret-do-not-use-in-prod") +os.environ.setdefault("WEAVER_KERNEL_SECRET", "example-secret-do-not-use-in-prod") -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, HMACTokenProvider, @@ -22,8 +22,8 @@ SensitivityTag, StaticRouter, ) -from agent_kernel.drivers.base import ExecutionContext -from agent_kernel.models import CapabilityRequest, ImplementationRef +from weaver_kernel.drivers.base import ExecutionContext +from weaver_kernel.models import CapabilityRequest, ImplementationRef def build_registry() -> CapabilityRegistry: diff --git a/examples/billing_demo.py b/examples/billing_demo.py index 7b2f8fa..dff4a2c 100644 --- a/examples/billing_demo.py +++ b/examples/billing_demo.py @@ -8,9 +8,9 @@ import asyncio import os -os.environ.setdefault("AGENT_KERNEL_SECRET", "example-secret-do-not-use-in-prod") +os.environ.setdefault("WEAVER_KERNEL_SECRET", "example-secret-do-not-use-in-prod") -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, Firewall, @@ -22,8 +22,8 @@ StaticRouter, make_billing_driver, ) -from agent_kernel.firewall.budgets import Budgets -from agent_kernel.models import CapabilityRequest, ImplementationRef +from weaver_kernel.firewall.budgets import Budgets +from weaver_kernel.models import CapabilityRequest, ImplementationRef def build_registry() -> CapabilityRegistry: diff --git a/examples/chainweaver_flow.py b/examples/chainweaver_flow.py index 00040ab..99acb52 100644 --- a/examples/chainweaver_flow.py +++ b/examples/chainweaver_flow.py @@ -34,7 +34,7 @@ from dataclasses import dataclass, field from typing import Any -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, HMACTokenProvider, @@ -43,9 +43,9 @@ SafetyClass, StaticRouter, ) -from agent_kernel.drivers.base import ExecutionContext -from agent_kernel.errors import DriverError -from agent_kernel.models import CapabilityRequest, ImplementationRef, RawResult +from weaver_kernel.drivers.base import ExecutionContext +from weaver_kernel.errors import DriverError +from weaver_kernel.models import CapabilityRequest, ImplementationRef, RawResult _SECRET = "example-secret-do-not-use-in-prod" @@ -58,7 +58,7 @@ class FlowExecutionError(Exception): Carries the orchestration context a real ChainWeaver error would: which flow failed and at which step. The :class:`ChainWeaverDriver` translates - this into a kernel :class:`~agent_kernel.errors.DriverError` so the context + this into a kernel :class:`~weaver_kernel.errors.DriverError` so the context survives into the audit trail instead of leaking a raw stack trace. """ @@ -110,7 +110,7 @@ class ChainWeaverDriver: Operations map to compiled flows. ``execute`` runs the flow named by ``ctx.args['operation']`` (falling back to ``ctx.capability_id``) with the remaining args as inputs. A :class:`FlowExecutionError` is re-raised as a - :class:`~agent_kernel.errors.DriverError` that keeps the flow id and failing + :class:`~weaver_kernel.errors.DriverError` that keeps the flow id and failing step, so the orchestration context is preserved for the caller and the audit trail. """ diff --git a/examples/contextweaver_policy_flow.py b/examples/contextweaver_policy_flow.py index d380c61..58f454d 100644 --- a/examples/contextweaver_policy_flow.py +++ b/examples/contextweaver_policy_flow.py @@ -30,7 +30,7 @@ import asyncio -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, HMACTokenProvider, @@ -40,10 +40,10 @@ SafetyClass, StaticRouter, ) -from agent_kernel.drivers.base import ExecutionContext -from agent_kernel.errors import PolicyDenied -from agent_kernel.models import CapabilityRequest, ImplementationRef -from agent_kernel.policy_reasons import DenialReason +from weaver_kernel.drivers.base import ExecutionContext +from weaver_kernel.errors import PolicyDenied +from weaver_kernel.models import CapabilityRequest, ImplementationRef +from weaver_kernel.policy_reasons import DenialReason _SECRET = "example-secret-do-not-use-in-prod" diff --git a/examples/evaluation_artifact_policy.py b/examples/evaluation_artifact_policy.py index bfc1ecb..18ef9d0 100644 --- a/examples/evaluation_artifact_policy.py +++ b/examples/evaluation_artifact_policy.py @@ -30,7 +30,7 @@ from dataclasses import dataclass, field from typing import Any -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, HMACTokenProvider, @@ -40,8 +40,8 @@ SafetyClass, StaticRouter, ) -from agent_kernel.drivers.base import ExecutionContext -from agent_kernel.models import CapabilityRequest, ImplementationRef +from weaver_kernel.drivers.base import ExecutionContext +from weaver_kernel.models import CapabilityRequest, ImplementationRef _SECRET = "example-secret-do-not-use-in-prod" diff --git a/examples/http_driver_demo.py b/examples/http_driver_demo.py index a3ce571..50d579c 100644 --- a/examples/http_driver_demo.py +++ b/examples/http_driver_demo.py @@ -14,9 +14,9 @@ import threading from http.server import BaseHTTPRequestHandler, HTTPServer -os.environ.setdefault("AGENT_KERNEL_SECRET", "example-secret-do-not-use-in-prod") +os.environ.setdefault("WEAVER_KERNEL_SECRET", "example-secret-do-not-use-in-prod") -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, HMACTokenProvider, @@ -25,8 +25,8 @@ SafetyClass, StaticRouter, ) -from agent_kernel.drivers.http import HTTPDriver, HTTPEndpoint -from agent_kernel.models import CapabilityRequest +from weaver_kernel.drivers.http import HTTPDriver, HTTPEndpoint +from weaver_kernel.models import CapabilityRequest # ── Tiny HTTP server ──────────────────────────────────────────────────────────── diff --git a/examples/readme_quickstart.py b/examples/readme_quickstart.py index 6b24cfe..8cfec57 100644 --- a/examples/readme_quickstart.py +++ b/examples/readme_quickstart.py @@ -13,9 +13,9 @@ import asyncio import os -os.environ.setdefault("AGENT_KERNEL_SECRET", "readme-quickstart-secret-not-for-prod") +os.environ.setdefault("WEAVER_KERNEL_SECRET", "readme-quickstart-secret-not-for-prod") -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, InMemoryDriver, @@ -24,7 +24,7 @@ SafetyClass, StaticRouter, ) -from agent_kernel.models import CapabilityRequest +from weaver_kernel.models import CapabilityRequest # 1. Register a capability registry = CapabilityRegistry() diff --git a/examples/repository_safety_check.py b/examples/repository_safety_check.py index 3b0580a..3232cf5 100644 --- a/examples/repository_safety_check.py +++ b/examples/repository_safety_check.py @@ -32,7 +32,7 @@ import sys import tempfile -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, HMACTokenProvider, @@ -42,9 +42,9 @@ SafetyClass, StaticRouter, ) -from agent_kernel.drivers.base import ExecutionContext -from agent_kernel.errors import DriverError -from agent_kernel.models import CapabilityRequest, ImplementationRef, RawResult +from weaver_kernel.drivers.base import ExecutionContext +from weaver_kernel.errors import DriverError +from weaver_kernel.models import CapabilityRequest, ImplementationRef, RawResult _SECRET = "example-secret-do-not-use-in-prod" diff --git a/examples/tutorial.py b/examples/tutorial.py index fecbcf8..765e389 100644 --- a/examples/tutorial.py +++ b/examples/tutorial.py @@ -24,9 +24,9 @@ import asyncio import os -os.environ.setdefault("AGENT_KERNEL_SECRET", "tutorial-secret-do-not-use-in-prod") +os.environ.setdefault("WEAVER_KERNEL_SECRET", "tutorial-secret-do-not-use-in-prod") -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, HMACTokenProvider, @@ -37,9 +37,9 @@ SensitivityTag, StaticRouter, ) -from agent_kernel.drivers.base import ExecutionContext -from agent_kernel.errors import HandleConstraintViolation, PolicyDenied -from agent_kernel.models import CapabilityRequest, ImplementationRef +from weaver_kernel.drivers.base import ExecutionContext +from weaver_kernel.errors import HandleConstraintViolation, PolicyDenied +from weaver_kernel.models import CapabilityRequest, ImplementationRef # A tiny, deterministic dataset that mixes safe and PII-bearing fields. # Email is present on purpose: the firewall must keep it out of the LLM-safe diff --git a/pyproject.toml b/pyproject.toml index c71f0d0..5b0cbaa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,20 @@ readme = "README.md" license = { file = "LICENSE" } requires-python = ">=3.10" authors = [{ name = "agent-kernel contributors" }] -keywords = ["ai", "agents", "mcp", "security", "capabilities", "llm"] +keywords = [ + "ai", + "agents", + "mcp", + "security", + "capabilities", + "capability-based-security", + "authorization", + "audit", + "llm", + "weaver-stack", + "weaver-kernel", + "agent-kernel", +] classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", @@ -55,7 +68,7 @@ policy = [ tiktoken = ["tiktoken>=0.6"] [tool.hatch.build.targets.wheel] -packages = ["src/agent_kernel"] +packages = ["src/weaver_kernel"] [tool.pytest.ini_options] asyncio_mode = "auto" diff --git a/src/agent_kernel/__init__.py b/src/weaver_kernel/__init__.py similarity index 87% rename from src/agent_kernel/__init__.py rename to src/weaver_kernel/__init__.py index 5b45dd8..e324f3f 100644 --- a/src/agent_kernel/__init__.py +++ b/src/weaver_kernel/__init__.py @@ -5,40 +5,40 @@ Core classes:: - from agent_kernel import Kernel, CapabilityRegistry - from agent_kernel import Capability, Principal - from agent_kernel import SafetyClass, SensitivityTag + from weaver_kernel import Kernel, CapabilityRegistry + from weaver_kernel import Capability, Principal + from weaver_kernel import SafetyClass, SensitivityTag Token management:: - from agent_kernel import HMACTokenProvider, CapabilityToken + from weaver_kernel import HMACTokenProvider, CapabilityToken Policy:: - from agent_kernel import DefaultPolicyEngine, DeclarativePolicyEngine - from agent_kernel import PolicyDecisionTrace, PolicyTraceStep - from agent_kernel import DenialReason, AllowReason + from weaver_kernel import DefaultPolicyEngine, DeclarativePolicyEngine + from weaver_kernel import PolicyDecisionTrace, PolicyTraceStep + from weaver_kernel import DenialReason, AllowReason Firewall:: - from agent_kernel import Firewall, Budgets, BudgetManager + from weaver_kernel import Firewall, Budgets, BudgetManager Handles & traces:: - from agent_kernel import HandleStore, TraceStore + from weaver_kernel import HandleStore, TraceStore LLM tool-format adapters:: - from agent_kernel import OpenAIMiddleware, AnthropicMiddleware + from weaver_kernel import OpenAIMiddleware, AnthropicMiddleware Federation (capability marketplace):: - from agent_kernel import CapabilityManifest, CapabilityDescriptor - from agent_kernel import build_manifest, import_manifest, TrustPolicy + from weaver_kernel import CapabilityManifest, CapabilityDescriptor + from weaver_kernel import build_manifest, import_manifest, TrustPolicy Errors:: - from agent_kernel import ( + from weaver_kernel import ( AgentKernelError, TokenExpired, TokenInvalid, TokenScopeError, TokenRevoked, PolicyDenied, PolicyConfigError, @@ -144,7 +144,7 @@ # Single source of truth: read the version from the installed distribution # metadata (the PyPI dist name is ``weaver-kernel``, distinct from the import -# name ``agent_kernel``) so it never drifts from ``pyproject.toml``. +# name ``weaver_kernel``) so it never drifts from ``pyproject.toml``. try: __version__ = _pkg_version("weaver-kernel") except PackageNotFoundError: # pragma: no cover - source tree without dist metadata diff --git a/src/agent_kernel/adapters/__init__.py b/src/weaver_kernel/adapters/__init__.py similarity index 92% rename from src/agent_kernel/adapters/__init__.py rename to src/weaver_kernel/adapters/__init__.py index ac20246..410bbab 100644 --- a/src/agent_kernel/adapters/__init__.py +++ b/src/weaver_kernel/adapters/__init__.py @@ -1,6 +1,6 @@ """LLM tool-format adapters and middleware. -The adapter layer translates between :class:`~agent_kernel.Capability` objects +The adapter layer translates between :class:`~weaver_kernel.Capability` objects and vendor-specific tool shapes (OpenAI Responses / Chat Completions, Anthropic Messages) without depending on the vendor SDKs at runtime. The middleware classes also route a vendor's tool-call objects through the full diff --git a/src/agent_kernel/adapters/_base.py b/src/weaver_kernel/adapters/_base.py similarity index 98% rename from src/agent_kernel/adapters/_base.py rename to src/weaver_kernel/adapters/_base.py index 8be85c3..573de72 100644 --- a/src/agent_kernel/adapters/_base.py +++ b/src/weaver_kernel/adapters/_base.py @@ -1,10 +1,10 @@ """Shared plumbing for LLM tool-format adapters. This module is private. Stable adapter API is exported from -:mod:`agent_kernel.adapters` (and re-exported from :mod:`agent_kernel`). +:mod:`weaver_kernel.adapters` (and re-exported from :mod:`weaver_kernel`). -Both :class:`~agent_kernel.adapters.openai.OpenAIMiddleware` and -:class:`~agent_kernel.adapters.anthropic.AnthropicMiddleware` build on top of +Both :class:`~weaver_kernel.adapters.openai.OpenAIMiddleware` and +:class:`~weaver_kernel.adapters.anthropic.AnthropicMiddleware` build on top of :class:`BaseToolMiddleware`, which owns: - hook registration and dispatch (sync or async callables) @@ -440,7 +440,7 @@ async def _await_if_needed(value: Any) -> None: await value -# Public re-exports so ``from agent_kernel.adapters import X`` resolves +# Public re-exports so ``from weaver_kernel.adapters import X`` resolves # cleanly even for internals subclasses lean on. __all__ = [ "BaseToolMiddleware", diff --git a/src/agent_kernel/adapters/anthropic.py b/src/weaver_kernel/adapters/anthropic.py similarity index 100% rename from src/agent_kernel/adapters/anthropic.py rename to src/weaver_kernel/adapters/anthropic.py diff --git a/src/agent_kernel/adapters/openai.py b/src/weaver_kernel/adapters/openai.py similarity index 99% rename from src/agent_kernel/adapters/openai.py rename to src/weaver_kernel/adapters/openai.py index 8608b0e..273a89d 100644 --- a/src/agent_kernel/adapters/openai.py +++ b/src/weaver_kernel/adapters/openai.py @@ -220,8 +220,8 @@ def format_result( """Wrap a payload dict in an OpenAI tool-result envelope. *payload* should already be the canonical kernel-result body produced by - :func:`agent_kernel.adapters._base.frame_to_payload` or - :func:`agent_kernel.adapters._base.error_to_payload`. + :func:`weaver_kernel.adapters._base.frame_to_payload` or + :func:`weaver_kernel.adapters._base.error_to_payload`. """ body = json.dumps(payload, ensure_ascii=False, sort_keys=True, default=str) if format == "chat_completions": diff --git a/src/agent_kernel/drivers/__init__.py b/src/weaver_kernel/drivers/__init__.py similarity index 100% rename from src/agent_kernel/drivers/__init__.py rename to src/weaver_kernel/drivers/__init__.py diff --git a/src/agent_kernel/drivers/base.py b/src/weaver_kernel/drivers/base.py similarity index 100% rename from src/agent_kernel/drivers/base.py rename to src/weaver_kernel/drivers/base.py diff --git a/src/agent_kernel/drivers/http.py b/src/weaver_kernel/drivers/http.py similarity index 100% rename from src/agent_kernel/drivers/http.py rename to src/weaver_kernel/drivers/http.py diff --git a/src/agent_kernel/drivers/mcp.py b/src/weaver_kernel/drivers/mcp.py similarity index 100% rename from src/agent_kernel/drivers/mcp.py rename to src/weaver_kernel/drivers/mcp.py diff --git a/src/agent_kernel/drivers/mcp_support.py b/src/weaver_kernel/drivers/mcp_support.py similarity index 100% rename from src/agent_kernel/drivers/mcp_support.py rename to src/weaver_kernel/drivers/mcp_support.py diff --git a/src/agent_kernel/drivers/memory.py b/src/weaver_kernel/drivers/memory.py similarity index 100% rename from src/agent_kernel/drivers/memory.py rename to src/weaver_kernel/drivers/memory.py diff --git a/src/agent_kernel/enums.py b/src/weaver_kernel/enums.py similarity index 100% rename from src/agent_kernel/enums.py rename to src/weaver_kernel/enums.py diff --git a/src/agent_kernel/errors.py b/src/weaver_kernel/errors.py similarity index 94% rename from src/agent_kernel/errors.py rename to src/weaver_kernel/errors.py index c3cd3c0..be7aa2b 100644 --- a/src/agent_kernel/errors.py +++ b/src/weaver_kernel/errors.py @@ -31,7 +31,7 @@ class PolicyDenied(AgentKernelError): """Raised when the policy engine rejects a capability request. Carries an optional ``reason_code`` attribute holding a stable - :class:`~agent_kernel.policy_reasons.DenialReason` value so callers can + :class:`~weaver_kernel.policy_reasons.DenialReason` value so callers can branch on it without matching the human-readable message: .. code-block:: python @@ -67,7 +67,7 @@ class FirewallError(AgentKernelError): class BudgetExhausted(AgentKernelError): - """Raised when a :class:`~agent_kernel.firewall.budget_manager.BudgetManager` has + """Raised when a :class:`~weaver_kernel.firewall.budget_manager.BudgetManager` has no remaining cross-invocation context budget. Distinct from :class:`FirewallError`: this error fires *before* the @@ -78,7 +78,7 @@ class BudgetExhausted(AgentKernelError): class BudgetConfigError(AgentKernelError): - """Raised when a :class:`~agent_kernel.firewall.budget_manager.BudgetManager` is + """Raised when a :class:`~weaver_kernel.firewall.budget_manager.BudgetManager` is constructed with invalid parameters, or asked to allocate/record/release a negative amount. @@ -132,7 +132,7 @@ class HandleConstraintViolation(AgentKernelError): """Raised when a handle expansion request violates the grant's constraints. Handles persist the constraints attached to the original - :class:`~agent_kernel.models.PolicyDecision` (e.g. ``max_rows``, + :class:`~weaver_kernel.models.PolicyDecision` (e.g. ``max_rows``, ``allowed_fields``). :meth:`HandleStore.expand` rechecks the requested query against those constraints; expansions that would exceed the row cap, request disallowed fields, or violate scope raise this error so @@ -171,7 +171,7 @@ class TrustPolicyError(FederationError): class ManifestError(FederationError): - """Raised when a :class:`~agent_kernel.models.CapabilityManifest` cannot be + """Raised when a :class:`~weaver_kernel.models.CapabilityManifest` cannot be serialised, parsed, or imported (e.g. missing fields, invalid version, duplicate capability IDs). """ diff --git a/src/agent_kernel/federation.py b/src/weaver_kernel/federation.py similarity index 94% rename from src/agent_kernel/federation.py rename to src/weaver_kernel/federation.py index 7f4d1db..9ca998c 100644 --- a/src/agent_kernel/federation.py +++ b/src/weaver_kernel/federation.py @@ -2,12 +2,12 @@ This module implements *part 1* of the capability marketplace protocol (issue #52): one kernel can advertise its capabilities as a -:class:`~agent_kernel.models.CapabilityManifest`, and a second kernel can +:class:`~weaver_kernel.models.CapabilityManifest`, and a second kernel can import that manifest to extend its own registry. Remote invocation is then performed by routing imported capabilities to a caller-supplied -:class:`~agent_kernel.drivers.base.Driver` (typically an -:class:`~agent_kernel.drivers.http.HTTPDriver` or -:class:`~agent_kernel.drivers.mcp.MCPDriver`) — every imported call still +:class:`~weaver_kernel.drivers.base.Driver` (typically an +:class:`~weaver_kernel.drivers.http.HTTPDriver` or +:class:`~weaver_kernel.drivers.mcp.MCPDriver`) — every imported call still flows through the *local* policy → token → firewall pipeline, satisfying weaver-spec I-01 / I-02 / I-06. @@ -45,7 +45,7 @@ Required by use cases that span trust boundaries. - ``"local_only"``: the importing kernel ignores the descriptor's sensitivity tag and registers the imported capability with - :attr:`~agent_kernel.enums.SensitivityTag.NONE`. Use when the importer + :attr:`~weaver_kernel.enums.SensitivityTag.NONE`. Use when the importer owns both kernels and has a single canonical policy. - ``"remote_deferred"``: the descriptor's sensitivity tag is preserved verbatim and treated as the remote policy's input; the importing kernel @@ -137,7 +137,7 @@ def import_manifest( Each descriptor becomes a regular :class:`Capability` whose :class:`ImplementationRef` points at the caller-supplied *driver_id*. The importing kernel must register a matching driver with - :meth:`~agent_kernel.Kernel.register_driver`. Invocations on the + :meth:`~weaver_kernel.Kernel.register_driver`. Invocations on the resulting capability flow through the full local pipeline — the remote endpoint is never trusted to perform policy, token verification, or firewall transformation on behalf of the importer. @@ -147,8 +147,8 @@ def import_manifest( registry: The local :class:`CapabilityRegistry` to extend. driver_id: The local driver ID that will execute imported capabilities. The caller is responsible for registering a driver with that ID - (typically an :class:`~agent_kernel.drivers.http.HTTPDriver` or - :class:`~agent_kernel.drivers.mcp.MCPDriver` configured for + (typically an :class:`~weaver_kernel.drivers.http.HTTPDriver` or + :class:`~weaver_kernel.drivers.mcp.MCPDriver` configured for ``manifest.endpoint``). trust_policy: How the importer weighs the manifest's sensitivity metadata. See :data:`TrustPolicy`. diff --git a/src/agent_kernel/federation_discovery.py b/src/weaver_kernel/federation_discovery.py similarity index 100% rename from src/agent_kernel/federation_discovery.py rename to src/weaver_kernel/federation_discovery.py diff --git a/src/agent_kernel/firewall/__init__.py b/src/weaver_kernel/firewall/__init__.py similarity index 100% rename from src/agent_kernel/firewall/__init__.py rename to src/weaver_kernel/firewall/__init__.py diff --git a/src/agent_kernel/firewall/budget_manager.py b/src/weaver_kernel/firewall/budget_manager.py similarity index 96% rename from src/agent_kernel/firewall/budget_manager.py rename to src/weaver_kernel/firewall/budget_manager.py index 3e8f19f..b6fb55d 100644 --- a/src/agent_kernel/firewall/budget_manager.py +++ b/src/weaver_kernel/firewall/budget_manager.py @@ -1,9 +1,9 @@ """Cross-invocation session-level budget manager. A :class:`BudgetManager` tracks cumulative token usage across multiple -:meth:`~agent_kernel.Kernel.invoke` calls and suggests -:class:`~agent_kernel.models.ResponseMode` escalation as the remaining budget -shrinks. The manager is optional — a :class:`~agent_kernel.Kernel` +:meth:`~weaver_kernel.Kernel.invoke` calls and suggests +:class:`~weaver_kernel.models.ResponseMode` escalation as the remaining budget +shrinks. The manager is optional — a :class:`~weaver_kernel.Kernel` constructed without one behaves identically to earlier versions of the library. @@ -36,7 +36,7 @@ class _BudgetState: class BudgetManager: """Tracks cumulative token usage across invocations within a session. - When attached to a :class:`~agent_kernel.Kernel` via the + When attached to a :class:`~weaver_kernel.Kernel` via the ``budget_manager`` constructor parameter, the kernel calls :meth:`allocate` before every invocation to reserve a slice of the remaining budget and :meth:`record_usage` after the firewall has @@ -197,7 +197,7 @@ async def release(self, reserved: int) -> None: """Release a reservation without recording any usage. Called when an invocation fails before the firewall runs (for - example the driver raised :class:`~agent_kernel.errors.DriverError` + example the driver raised :class:`~weaver_kernel.errors.DriverError` or the firewall itself raised). Args: diff --git a/src/agent_kernel/firewall/budgets.py b/src/weaver_kernel/firewall/budgets.py similarity index 76% rename from src/agent_kernel/firewall/budgets.py rename to src/weaver_kernel/firewall/budgets.py index 0db7fc5..1699825 100644 --- a/src/agent_kernel/firewall/budgets.py +++ b/src/weaver_kernel/firewall/budgets.py @@ -1,9 +1,9 @@ """Per-invocation firewall budget caps. Defines :class:`Budgets`, the dataclass enforced by the -:class:`~agent_kernel.firewall.transform.Firewall` when shaping a single -:class:`~agent_kernel.models.Frame`. Cross-invocation cumulative tracking -lives in :mod:`agent_kernel.firewall.budget_manager`. +:class:`~weaver_kernel.firewall.transform.Firewall` when shaping a single +:class:`~weaver_kernel.models.Frame`. Cross-invocation cumulative tracking +lives in :mod:`weaver_kernel.firewall.budget_manager`. """ from __future__ import annotations diff --git a/src/agent_kernel/firewall/redaction.py b/src/weaver_kernel/firewall/redaction.py similarity index 100% rename from src/agent_kernel/firewall/redaction.py rename to src/weaver_kernel/firewall/redaction.py diff --git a/src/agent_kernel/firewall/summarize.py b/src/weaver_kernel/firewall/summarize.py similarity index 100% rename from src/agent_kernel/firewall/summarize.py rename to src/weaver_kernel/firewall/summarize.py diff --git a/src/agent_kernel/firewall/token_counting.py b/src/weaver_kernel/firewall/token_counting.py similarity index 93% rename from src/agent_kernel/firewall/token_counting.py rename to src/weaver_kernel/firewall/token_counting.py index 8662383..35b5179 100644 --- a/src/agent_kernel/firewall/token_counting.py +++ b/src/weaver_kernel/firewall/token_counting.py @@ -2,7 +2,7 @@ The :class:`TokenCounter` protocol lets callers plug in vendor-specific token counters (for example, a ``tiktoken``-based one) into the -:class:`~agent_kernel.firewall.budget_manager.BudgetManager`. The default +:class:`~weaver_kernel.firewall.budget_manager.BudgetManager`. The default implementation, :func:`default_token_counter`, uses ``len(json.dumps(value, default=str)) // 4`` and has no extra dependencies. """ diff --git a/src/agent_kernel/firewall/transform.py b/src/weaver_kernel/firewall/transform.py similarity index 100% rename from src/agent_kernel/firewall/transform.py rename to src/weaver_kernel/firewall/transform.py diff --git a/src/agent_kernel/handles.py b/src/weaver_kernel/handles.py similarity index 100% rename from src/agent_kernel/handles.py rename to src/weaver_kernel/handles.py diff --git a/src/agent_kernel/kernel/__init__.py b/src/weaver_kernel/kernel/__init__.py similarity index 99% rename from src/agent_kernel/kernel/__init__.py rename to src/weaver_kernel/kernel/__init__.py index 8e7d967..07bf004 100644 --- a/src/agent_kernel/kernel/__init__.py +++ b/src/weaver_kernel/kernel/__init__.py @@ -1,7 +1,7 @@ """The :class:`Kernel` — the main entry point for agent-kernel. The class lives in this package's ``__init__.py`` so existing imports -(``from agent_kernel.kernel import Kernel``) continue to work after the +(``from weaver_kernel.kernel import Kernel``) continue to work after the split from a single-file module into a sub-package. Heavy implementation is delegated to sibling modules (:mod:`._invoke`, :mod:`._dry_run`) to honour AGENTS.md's ≤ 300-line module budget. @@ -249,7 +249,7 @@ async def invoke_stream( Yields :class:`Frame` chunks as they arrive from the driver. The last yielded frame has ``is_final=True``. Falls back to wrapping a single-shot :meth:`Driver.execute` when the resolved driver does not - implement :class:`~agent_kernel.drivers.base.StreamingDriver`. + implement :class:`~weaver_kernel.drivers.base.StreamingDriver`. The same security pipeline applies as in :meth:`invoke`: token verification, admin-mode gate, budget escalation, firewall diff --git a/src/agent_kernel/kernel/_dry_run.py b/src/weaver_kernel/kernel/_dry_run.py similarity index 100% rename from src/agent_kernel/kernel/_dry_run.py rename to src/weaver_kernel/kernel/_dry_run.py diff --git a/src/agent_kernel/kernel/_federation.py b/src/weaver_kernel/kernel/_federation.py similarity index 98% rename from src/agent_kernel/kernel/_federation.py rename to src/weaver_kernel/kernel/_federation.py index 646d379..37aaca7 100644 --- a/src/agent_kernel/kernel/_federation.py +++ b/src/weaver_kernel/kernel/_federation.py @@ -23,7 +23,7 @@ if TYPE_CHECKING: # pragma: no cover from . import Kernel -logger = logging.getLogger("agent_kernel.kernel") +logger = logging.getLogger("weaver_kernel.kernel") def perform_advertise( diff --git a/src/agent_kernel/kernel/_invoke.py b/src/weaver_kernel/kernel/_invoke.py similarity index 98% rename from src/agent_kernel/kernel/_invoke.py rename to src/weaver_kernel/kernel/_invoke.py index bc14a3e..b8d22ca 100644 --- a/src/agent_kernel/kernel/_invoke.py +++ b/src/weaver_kernel/kernel/_invoke.py @@ -39,7 +39,7 @@ if TYPE_CHECKING: # pragma: no cover from . import Kernel -logger = logging.getLogger("agent_kernel.kernel") +logger = logging.getLogger("weaver_kernel.kernel") _MEMORY_CAPABILITY_PREFIX = "memory." _MEMORY_SENSITIVE_ARG_KEYS: frozenset[str] = frozenset( @@ -72,9 +72,9 @@ def _frame_result_summary(frame: Frame) -> dict[str, Any]: Records only counts and flags taken from the already-transformed Frame — never raw driver data — so it preserves the I-01 boundary the Firewall enforces and keeps sensitive payloads out of the audit trail. Stored on - :attr:`~agent_kernel.models.ActionTrace.result_summary` so an invocation's + :attr:`~weaver_kernel.models.ActionTrace.result_summary` so an invocation's outcome (e.g. a safety check's pass/block decision) is auditable via - :meth:`~agent_kernel.Kernel.explain`. + :meth:`~weaver_kernel.Kernel.explain`. """ return { "fact_count": len(frame.facts), diff --git a/src/agent_kernel/kernel/_stream.py b/src/weaver_kernel/kernel/_stream.py similarity index 98% rename from src/agent_kernel/kernel/_stream.py rename to src/weaver_kernel/kernel/_stream.py index cfdcc3a..9691b00 100644 --- a/src/agent_kernel/kernel/_stream.py +++ b/src/weaver_kernel/kernel/_stream.py @@ -7,7 +7,7 @@ whole stream. When the resolved driver does not implement -:class:`~agent_kernel.drivers.base.StreamingDriver`, this helper falls +:class:`~weaver_kernel.drivers.base.StreamingDriver`, this helper falls back to a single :meth:`Driver.execute` call and yields one ``Frame`` with ``is_final=True``. The fallback preserves the same firewall + trace guarantees as the streaming path. @@ -39,7 +39,7 @@ if TYPE_CHECKING: # pragma: no cover from . import Kernel -logger = logging.getLogger("agent_kernel.kernel") +logger = logging.getLogger("weaver_kernel.kernel") async def invoke_stream_impl( diff --git a/src/agent_kernel/models.py b/src/weaver_kernel/models.py similarity index 95% rename from src/agent_kernel/models.py rename to src/weaver_kernel/models.py index 4d47512..d511eed 100644 --- a/src/agent_kernel/models.py +++ b/src/weaver_kernel/models.py @@ -40,7 +40,7 @@ class ImplementationRef: class ToolHints: """Vendor-specific tool-definition hints for LLM adapters. - Consumed by ``agent_kernel.adapters`` when emitting tool schemas. + Consumed by ``weaver_kernel.adapters`` when emitting tool schemas. Engines that don't recognise a hint silently ignore it; setting a hint never changes how the kernel itself behaves. """ @@ -194,8 +194,8 @@ class PolicyTraceStep: """Human-readable detail, e.g. ``"role 'writer' required, principal had ['reader']"``.""" reason_code: str | None = None - """For ``"denied"`` steps, the :class:`~agent_kernel.policy_reasons.DenialReason`. - For ``"allowed"`` steps, the :class:`~agent_kernel.policy_reasons.AllowReason`. + """For ``"denied"`` steps, the :class:`~weaver_kernel.policy_reasons.DenialReason`. + For ``"allowed"`` steps, the :class:`~weaver_kernel.policy_reasons.AllowReason`. ``None`` for ``"matched"``, ``"skipped"``, and ``"constraint_applied"`` steps. """ @@ -233,8 +233,8 @@ class PolicyDecisionTrace: """The decision the engine reached.""" final_reason_code: str | None = None - """The :class:`~agent_kernel.policy_reasons.AllowReason` or - :class:`~agent_kernel.policy_reasons.DenialReason` for the final outcome. + """The :class:`~weaver_kernel.policy_reasons.AllowReason` or + :class:`~weaver_kernel.policy_reasons.DenialReason` for the final outcome. """ @@ -253,8 +253,8 @@ class PolicyDecision: """Any additional constraints imposed by the policy (e.g. ``max_rows``).""" reason_code: str | None = None - """Stable machine-readable code (typically a :class:`~agent_kernel.policy_reasons.AllowReason` - or :class:`~agent_kernel.policy_reasons.DenialReason` value). + """Stable machine-readable code (typically a :class:`~weaver_kernel.policy_reasons.AllowReason` + or :class:`~weaver_kernel.policy_reasons.DenialReason` value). Use this for assertions, metrics, and UI mapping. ``None`` only when an out-of-tree policy engine has not populated it. @@ -321,7 +321,7 @@ class Handle: Handles carry the grant constraints persisted at creation time. The :class:`HandleStore` rechecks those constraints when the handle is expanded, so an over-broad expand query is denied with a stable - :class:`~agent_kernel.errors.HandleConstraintViolation` rather than + :class:`~weaver_kernel.errors.HandleConstraintViolation` rather than silently returning data the original grant never authorised. """ @@ -392,8 +392,8 @@ class Frame: is_final: bool = False """``True`` when this Frame is the last chunk of a stream. - Non-streaming :meth:`~agent_kernel.Kernel.invoke` always returns a Frame - with ``is_final=True``. For :meth:`~agent_kernel.Kernel.invoke_stream`, only + Non-streaming :meth:`~weaver_kernel.Kernel.invoke` always returns a Frame + with ``is_final=True``. For :meth:`~weaver_kernel.Kernel.invoke_stream`, only the terminal Frame has it set; intermediate chunks have ``is_final=False``. """ @@ -422,7 +422,7 @@ class ActionTrace: Derived **only** from the post-Firewall Frame — counts and flags, never raw driver data — so recording it cannot widen the I-01 boundary or leak sensitive payloads into the audit trail. It lets a reviewer reconstruct an - invocation's outcome directly from :meth:`~agent_kernel.Kernel.explain`; for + invocation's outcome directly from :meth:`~weaver_kernel.Kernel.explain`; for example, a repository safety check passed iff ``result_summary["row_count"] == 0``. """ @@ -448,7 +448,7 @@ class FailedCondition: """Actionable remediation hint.""" reason_code: str | None = None - """Stable machine-readable code (a :class:`~agent_kernel.policy_reasons.DenialReason` value). + """Stable machine-readable code (a :class:`~weaver_kernel.policy_reasons.DenialReason` value). Use this for assertions instead of matching the human-readable :attr:`suggestion` string. """ @@ -474,7 +474,7 @@ class DenialExplanation: """Human-readable single-sentence summary.""" reason_code: str | None = None - """Primary :class:`~agent_kernel.policy_reasons.DenialReason` for the denial + """Primary :class:`~weaver_kernel.policy_reasons.DenialReason` for the denial (typically the code of the first :class:`FailedCondition`). ``None`` on the allow path (``denied=False``). """ @@ -690,7 +690,7 @@ def from_dict(cls, data: dict[str, Any]) -> CapabilityManifest: class DryRunResult: """Result of a dry-run invocation — driver is never called. - Returned by :meth:`~agent_kernel.Kernel.invoke` when ``dry_run=True``. + Returned by :meth:`~weaver_kernel.Kernel.invoke` when ``dry_run=True``. """ capability_id: str diff --git a/src/agent_kernel/otel.py b/src/weaver_kernel/otel.py similarity index 86% rename from src/agent_kernel/otel.py rename to src/weaver_kernel/otel.py index 33bcf60..e21a688 100644 --- a/src/agent_kernel/otel.py +++ b/src/weaver_kernel/otel.py @@ -14,19 +14,19 @@ ``invoke()`` produces:: - agent_kernel.invoke + weaver_kernel.invoke ├── attributes: principal_id, capability_id, safety_class, │ response_mode, dry_run - ├── agent_kernel.driver.execute (per driver attempt) - └── agent_kernel.firewall.apply + ├── weaver_kernel.driver.execute (per driver attempt) + └── weaver_kernel.firewall.apply Metrics ------- -* ``agent_kernel.invocations`` (counter) — labels: +* ``weaver_kernel.invocations`` (counter) — labels: ``capability_id``, ``status`` (``success``/``error``/``denied``). -* ``agent_kernel.invocation_duration`` (histogram, milliseconds). -* ``agent_kernel.policy_denials`` (counter) — labels: ``capability_id``, +* ``weaver_kernel.invocation_duration`` (histogram, milliseconds). +* ``weaver_kernel.policy_denials`` (counter) — labels: ``capability_id``, ``reason_code``. Usage @@ -34,7 +34,7 @@ .. code-block:: python - from agent_kernel import Kernel, instrument_kernel + from weaver_kernel import Kernel, instrument_kernel kernel = Kernel(registry=...) instrument_kernel(kernel) # idempotent — calling again is a no-op. @@ -83,13 +83,13 @@ # Attribute keys (re-used across spans). Kept as module constants so a # downstream search/grep finds every emission site. -ATTR_PRINCIPAL = "agent_kernel.principal_id" -ATTR_CAPABILITY = "agent_kernel.capability_id" -ATTR_SAFETY_CLASS = "agent_kernel.safety_class" -ATTR_RESPONSE_MODE = "agent_kernel.response_mode" -ATTR_DRY_RUN = "agent_kernel.dry_run" -ATTR_DRIVER_ID = "agent_kernel.driver_id" -ATTR_REASON_CODE = "agent_kernel.reason_code" +ATTR_PRINCIPAL = "weaver_kernel.principal_id" +ATTR_CAPABILITY = "weaver_kernel.capability_id" +ATTR_SAFETY_CLASS = "weaver_kernel.safety_class" +ATTR_RESPONSE_MODE = "weaver_kernel.response_mode" +ATTR_DRY_RUN = "weaver_kernel.dry_run" +ATTR_DRIVER_ID = "weaver_kernel.driver_id" +ATTR_REASON_CODE = "weaver_kernel.reason_code" # Module-level cache so repeat :func:`instrument_kernel` calls are # cheap idempotent no-ops on the same instance. A WeakSet keys on the @@ -130,24 +130,24 @@ def instrument_kernel( _INSTRUMENTED.add(kernel) if tracer_provider is not None: - tracer = tracer_provider.get_tracer("agent_kernel") + tracer = tracer_provider.get_tracer("weaver_kernel") else: - tracer = trace.get_tracer("agent_kernel") + tracer = trace.get_tracer("weaver_kernel") if meter_provider is not None: - meter = meter_provider.get_meter("agent_kernel") + meter = meter_provider.get_meter("weaver_kernel") else: - meter = metrics.get_meter("agent_kernel") + meter = metrics.get_meter("weaver_kernel") invocations = meter.create_counter( - "agent_kernel.invocations", + "weaver_kernel.invocations", description="Count of Kernel.invoke calls, labeled by status", ) duration_hist = meter.create_histogram( - "agent_kernel.invocation_duration", + "weaver_kernel.invocation_duration", unit="ms", description="Latency of Kernel.invoke (milliseconds)", ) denials = meter.create_counter( - "agent_kernel.policy_denials", + "weaver_kernel.policy_denials", description="Count of policy denials, labeled by reason_code", ) @@ -169,7 +169,7 @@ async def instrumented_invoke( ATTR_RESPONSE_MODE: response_mode, ATTR_DRY_RUN: dry_run, } - with tracer.start_as_current_span("agent_kernel.invoke", attributes=attributes) as span: + with tracer.start_as_current_span("weaver_kernel.invoke", attributes=attributes) as span: try: # ``response_mode`` here is a runtime str, so mypy can't pick the # right overload of ``Kernel.invoke``. The wrapper preserves the @@ -209,7 +209,7 @@ def instrumented_grant( ATTR_PRINCIPAL: principal.principal_id, ATTR_CAPABILITY: request.capability_id, } - with tracer.start_as_current_span("agent_kernel.grant", attributes=attributes) as span: + with tracer.start_as_current_span("weaver_kernel.grant", attributes=attributes) as span: try: return original_grant(request, principal, justification=justification) except Exception as exc: diff --git a/src/agent_kernel/policy.py b/src/weaver_kernel/policy.py similarity index 99% rename from src/agent_kernel/policy.py rename to src/weaver_kernel/policy.py index e6af3bf..2d7dde3 100644 --- a/src/agent_kernel/policy.py +++ b/src/weaver_kernel/policy.py @@ -77,7 +77,7 @@ class ExplainingPolicyEngine(PolicyEngine, Protocol): answer :meth:`Kernel.explain_denial`. Both built-in engines (:class:`DefaultPolicyEngine` and - :class:`agent_kernel.DeclarativePolicyEngine`) satisfy this protocol. + :class:`weaver_kernel.DeclarativePolicyEngine`) satisfy this protocol. """ def explain( diff --git a/src/agent_kernel/policy_dsl.py b/src/weaver_kernel/policy_dsl.py similarity index 99% rename from src/agent_kernel/policy_dsl.py rename to src/weaver_kernel/policy_dsl.py index 2f3a507..e3dee13 100644 --- a/src/agent_kernel/policy_dsl.py +++ b/src/weaver_kernel/policy_dsl.py @@ -4,7 +4,7 @@ (:mod:`policy_dsl_parser`, :mod:`policy_dsl_explain`) so each module stays ≤ 300 lines per AGENTS.md. :class:`PolicyMatch` and :class:`PolicyRule` are re-exported from this module for backwards compatibility with the -public API surface (``from agent_kernel import PolicyMatch, PolicyRule``). +public API surface (``from weaver_kernel import PolicyMatch, PolicyRule``). """ from __future__ import annotations diff --git a/src/agent_kernel/policy_dsl_explain.py b/src/weaver_kernel/policy_dsl_explain.py similarity index 100% rename from src/agent_kernel/policy_dsl_explain.py rename to src/weaver_kernel/policy_dsl_explain.py diff --git a/src/agent_kernel/policy_dsl_parser.py b/src/weaver_kernel/policy_dsl_parser.py similarity index 99% rename from src/agent_kernel/policy_dsl_parser.py rename to src/weaver_kernel/policy_dsl_parser.py index 3d537ac..d8f5571 100644 --- a/src/agent_kernel/policy_dsl_parser.py +++ b/src/weaver_kernel/policy_dsl_parser.py @@ -155,7 +155,7 @@ def load_yaml_data(path: Path) -> dict[str, Any]: """Read a YAML file into a top-level mapping. Requires ``pyyaml``: ``pip install 'weaver-kernel[policy]'``. The import is - deferred so that ``import agent_kernel`` works without the policy extra. + deferred so that ``import weaver_kernel`` works without the policy extra. Raises: PolicyConfigError: If the file is unreadable, malformed, or pyyaml diff --git a/src/agent_kernel/policy_reasons.py b/src/weaver_kernel/policy_reasons.py similarity index 98% rename from src/agent_kernel/policy_reasons.py rename to src/weaver_kernel/policy_reasons.py index af318e8..8c00f35 100644 --- a/src/agent_kernel/policy_reasons.py +++ b/src/weaver_kernel/policy_reasons.py @@ -33,7 +33,7 @@ class DenialReason(_StrEnumCompat): Used as :attr:`PolicyDecision.reason_code`, :attr:`DenialExplanation.reason_code`, :attr:`FailedCondition.reason_code`, - and :attr:`agent_kernel.PolicyDenied.reason_code`. + and :attr:`weaver_kernel.PolicyDenied.reason_code`. """ # Role / identity diff --git a/src/agent_kernel/py.typed b/src/weaver_kernel/py.typed similarity index 100% rename from src/agent_kernel/py.typed rename to src/weaver_kernel/py.typed diff --git a/src/agent_kernel/rate_limit.py b/src/weaver_kernel/rate_limit.py similarity index 100% rename from src/agent_kernel/rate_limit.py rename to src/weaver_kernel/rate_limit.py diff --git a/src/agent_kernel/registry.py b/src/weaver_kernel/registry.py similarity index 99% rename from src/agent_kernel/registry.py rename to src/weaver_kernel/registry.py index 9ea5c69..c5f2bd2 100644 --- a/src/agent_kernel/registry.py +++ b/src/weaver_kernel/registry.py @@ -2,7 +2,7 @@ Supports dot-notation namespaces (``"billing.invoices.list"``) and deferred namespace loaders for large tool ecosystems; ranked search is delegated to -:mod:`agent_kernel.search_index`. Flat capability IDs continue to work — they +:mod:`weaver_kernel.search_index`. Flat capability IDs continue to work — they live in a single-segment namespace named after themselves. """ diff --git a/src/agent_kernel/router.py b/src/weaver_kernel/router.py similarity index 100% rename from src/agent_kernel/router.py rename to src/weaver_kernel/router.py diff --git a/src/agent_kernel/search_index.py b/src/weaver_kernel/search_index.py similarity index 98% rename from src/agent_kernel/search_index.py rename to src/weaver_kernel/search_index.py index 1edcce4..f9f37b7 100644 --- a/src/agent_kernel/search_index.py +++ b/src/weaver_kernel/search_index.py @@ -1,6 +1,6 @@ """BM25-flavoured scoring index for capability search. -Extracted from :mod:`agent_kernel.registry` to keep both modules within the +Extracted from :mod:`weaver_kernel.registry` to keep both modules within the AGENTS.md 300-line budget. Scoring is pure and deterministic — no randomness is used in matching (AGENTS.md). Matches on ``capability_id`` and ``tags`` are weighted above ``description``. diff --git a/src/agent_kernel/tokens.py b/src/weaver_kernel/tokens.py similarity index 98% rename from src/agent_kernel/tokens.py rename to src/weaver_kernel/tokens.py index 51867b8..16b7a8b 100644 --- a/src/agent_kernel/tokens.py +++ b/src/weaver_kernel/tokens.py @@ -29,16 +29,16 @@ def _get_secret() -> str: the fallback secret. """ global _DEV_SECRET - secret = os.environ.get("AGENT_KERNEL_SECRET") + secret = os.environ.get("WEAVER_KERNEL_SECRET") if secret: return secret with _DEV_SECRET_LOCK: if _DEV_SECRET is None: _DEV_SECRET = secrets.token_hex(32) logger.warning( - "AGENT_KERNEL_SECRET is not set. " + "WEAVER_KERNEL_SECRET is not set. " "Using a random development secret — tokens will not survive restarts. " - "Set AGENT_KERNEL_SECRET in production." + "Set WEAVER_KERNEL_SECRET in production." ) return _DEV_SECRET @@ -185,7 +185,7 @@ def revoke_all(self, principal_id: str) -> int: class HMACTokenProvider: """Issues and verifies HMAC-SHA256 capability tokens. - The signing secret is read from the ``AGENT_KERNEL_SECRET`` environment + The signing secret is read from the ``WEAVER_KERNEL_SECRET`` environment variable. If the variable is absent a random development secret is generated and a warning is logged. """ diff --git a/src/agent_kernel/trace.py b/src/weaver_kernel/trace.py similarity index 94% rename from src/agent_kernel/trace.py rename to src/weaver_kernel/trace.py index ccbd2bd..cd94d96 100644 --- a/src/agent_kernel/trace.py +++ b/src/weaver_kernel/trace.py @@ -9,7 +9,7 @@ class TraceStore: """Stores :class:`ActionTrace` records indexed by ``action_id``. - All invocations recorded by the :class:`~agent_kernel.kernel.Kernel` are + All invocations recorded by the :class:`~weaver_kernel.kernel.Kernel` are retrievable here for audit and explainability purposes. """ diff --git a/tests/conftest.py b/tests/conftest.py index 0f74758..b3d0d88 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,7 +4,7 @@ import pytest -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, HMACTokenProvider, @@ -16,8 +16,8 @@ StaticRouter, make_billing_driver, ) -from agent_kernel.drivers.base import ExecutionContext -from agent_kernel.models import ImplementationRef +from weaver_kernel.drivers.base import ExecutionContext +from weaver_kernel.models import ImplementationRef # ── Capabilities ─────────────────────────────────────────────────────────────── diff --git a/tests/test_adapters.py b/tests/test_adapters.py index 1bf90f6..a03125f 100644 --- a/tests/test_adapters.py +++ b/tests/test_adapters.py @@ -13,7 +13,7 @@ import pytest from pydantic import BaseModel, Field -from agent_kernel import ( +from weaver_kernel import ( AdapterParseError, AnthropicMiddleware, Capability, @@ -25,17 +25,17 @@ SensitivityTag, ToolHints, ) -from agent_kernel.adapters import ( +from weaver_kernel.adapters import ( ToolCallEvent, ToolResultEvent, ) -from agent_kernel.adapters import ( +from weaver_kernel.adapters import ( anthropic as anthropic_mod, ) -from agent_kernel.adapters import ( +from weaver_kernel.adapters import ( openai as openai_mod, ) -from agent_kernel.adapters._base import ( +from weaver_kernel.adapters._base import ( build_input_schema, error_to_payload, frame_to_payload, @@ -44,7 +44,7 @@ restore_namespace, validate_input, ) -from agent_kernel.models import CapabilityRequest +from weaver_kernel.models import CapabilityRequest # ── Helpers ─────────────────────────────────────────────────────────────────── @@ -865,8 +865,8 @@ async def test_middleware_input_validation_surfaces_as_tool_error( registry = CapabilityRegistry() registry.register(cap) - from agent_kernel import HMACTokenProvider, InMemoryDriver, StaticRouter - from agent_kernel.drivers.base import ExecutionContext + from weaver_kernel import HMACTokenProvider, InMemoryDriver, StaticRouter + from weaver_kernel.drivers.base import ExecutionContext driver = InMemoryDriver(driver_id="memory") @@ -936,7 +936,7 @@ def bad(event: ToolResultEvent) -> None: mw = OpenAIMiddleware(kernel, reader_principal) mw.intercept_tool_result(bad) - with caplog.at_level("WARNING", logger="agent_kernel.adapters._base"): + with caplog.at_level("WARNING", logger="weaver_kernel.adapters._base"): outputs = await mw.handle_tool_calls( [ { @@ -956,9 +956,9 @@ def bad(event: ToolResultEvent) -> None: @pytest.mark.asyncio async def test_driver_error_surfaces_as_tool_error(reader_principal: Principal) -> None: """A DriverError is converted to a tool-result error, not raised.""" - from agent_kernel import HMACTokenProvider, InMemoryDriver, StaticRouter - from agent_kernel.drivers.base import ExecutionContext - from agent_kernel.errors import DriverError + from weaver_kernel import HMACTokenProvider, InMemoryDriver, StaticRouter + from weaver_kernel.drivers.base import ExecutionContext + from weaver_kernel.errors import DriverError registry = CapabilityRegistry() registry.register( @@ -1100,7 +1100,7 @@ def test_frame_to_payload_shape(kernel: Kernel) -> None: """frame_to_payload returns the canonical JSON shape both adapters share.""" import datetime - from agent_kernel.models import Frame, Handle + from weaver_kernel.models import Frame, Handle handle = Handle( handle_id="h-1", diff --git a/tests/test_chainweaver_flow.py b/tests/test_chainweaver_flow.py index b8a0338..6d8955c 100644 --- a/tests/test_chainweaver_flow.py +++ b/tests/test_chainweaver_flow.py @@ -16,8 +16,8 @@ import pytest -from agent_kernel import Principal -from agent_kernel.errors import DriverError +from weaver_kernel import Principal +from weaver_kernel.errors import DriverError _EXAMPLES = Path(__file__).resolve().parent.parent / "examples" diff --git a/tests/test_drivers.py b/tests/test_drivers.py index a578791..35ba4f4 100644 --- a/tests/test_drivers.py +++ b/tests/test_drivers.py @@ -8,9 +8,9 @@ import httpx import pytest -from agent_kernel import DriverError, InMemoryDriver -from agent_kernel.drivers.base import ExecutionContext -from agent_kernel.drivers.http import HTTPDriver, HTTPEndpoint +from weaver_kernel import DriverError, InMemoryDriver +from weaver_kernel.drivers.base import ExecutionContext +from weaver_kernel.drivers.http import HTTPDriver, HTTPEndpoint # ── InMemoryDriver ───────────────────────────────────────────────────────────── @@ -139,7 +139,7 @@ async def test_httpdriver_execute_get(monkeypatch: pytest.MonkeyPatch) -> None: mock_client.__aexit__ = AsyncMock(return_value=False) mock_client.get = AsyncMock(return_value=mock_response) - with patch("agent_kernel.drivers.http.httpx.AsyncClient", return_value=mock_client): + with patch("weaver_kernel.drivers.http.httpx.AsyncClient", return_value=mock_client): ctx = ExecutionContext( capability_id="cap.x", principal_id="u1", @@ -174,7 +174,7 @@ async def test_httpdriver_http_error_raises(monkeypatch: pytest.MonkeyPatch) -> error = httpx.HTTPStatusError("Server Error", request=MagicMock(), response=mock_response) mock_client.get = AsyncMock(side_effect=error) - with patch("agent_kernel.drivers.http.httpx.AsyncClient", return_value=mock_client): + with patch("weaver_kernel.drivers.http.httpx.AsyncClient", return_value=mock_client): ctx = ExecutionContext( capability_id="cap.x", principal_id="u1", @@ -200,7 +200,7 @@ async def test_httpdriver_execute_post() -> None: mock_client.__aexit__ = AsyncMock(return_value=False) mock_client.post = AsyncMock(return_value=mock_response) - with patch("agent_kernel.drivers.http.httpx.AsyncClient", return_value=mock_client): + with patch("weaver_kernel.drivers.http.httpx.AsyncClient", return_value=mock_client): ctx = ExecutionContext( capability_id="cap.x", principal_id="u1", @@ -227,7 +227,7 @@ async def test_httpdriver_execute_put() -> None: mock_client.__aexit__ = AsyncMock(return_value=False) mock_client.put = AsyncMock(return_value=mock_response) - with patch("agent_kernel.drivers.http.httpx.AsyncClient", return_value=mock_client): + with patch("weaver_kernel.drivers.http.httpx.AsyncClient", return_value=mock_client): ctx = ExecutionContext( capability_id="cap.x", principal_id="u1", @@ -254,7 +254,7 @@ async def test_httpdriver_execute_delete() -> None: mock_client.__aexit__ = AsyncMock(return_value=False) mock_client.delete = AsyncMock(return_value=mock_response) - with patch("agent_kernel.drivers.http.httpx.AsyncClient", return_value=mock_client): + with patch("weaver_kernel.drivers.http.httpx.AsyncClient", return_value=mock_client): ctx = ExecutionContext( capability_id="cap.x", principal_id="u1", @@ -281,7 +281,7 @@ async def test_httpdriver_execute_patch_uses_request() -> None: mock_client.__aexit__ = AsyncMock(return_value=False) mock_client.request = AsyncMock(return_value=mock_response) - with patch("agent_kernel.drivers.http.httpx.AsyncClient", return_value=mock_client): + with patch("weaver_kernel.drivers.http.httpx.AsyncClient", return_value=mock_client): ctx = ExecutionContext( capability_id="cap.x", principal_id="u1", @@ -307,7 +307,7 @@ async def test_httpdriver_request_error_raises() -> None: side_effect=httpx.ConnectError("Connection refused", request=MagicMock()) ) - with patch("agent_kernel.drivers.http.httpx.AsyncClient", return_value=mock_client): + with patch("weaver_kernel.drivers.http.httpx.AsyncClient", return_value=mock_client): ctx = ExecutionContext( capability_id="cap.x", principal_id="u1", diff --git a/tests/test_evaluation_artifact_policy.py b/tests/test_evaluation_artifact_policy.py index 90c7a44..77d2ac1 100644 --- a/tests/test_evaluation_artifact_policy.py +++ b/tests/test_evaluation_artifact_policy.py @@ -14,7 +14,7 @@ import pytest -from agent_kernel import Principal +from weaver_kernel import Principal _EXAMPLES = Path(__file__).resolve().parent.parent / "examples" diff --git a/tests/test_federation.py b/tests/test_federation.py index b233d8f..4c7dd8b 100644 --- a/tests/test_federation.py +++ b/tests/test_federation.py @@ -6,7 +6,7 @@ import pytest -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityDescriptor, CapabilityManifest, @@ -26,9 +26,9 @@ import_manifest, merge_sensitivity, ) -from agent_kernel.drivers.base import ExecutionContext -from agent_kernel.federation import MANIFEST_VERSION -from agent_kernel.models import CapabilityRequest +from weaver_kernel.drivers.base import ExecutionContext +from weaver_kernel.federation import MANIFEST_VERSION +from weaver_kernel.models import CapabilityRequest # ── Helpers ─────────────────────────────────────────────────────────────────── @@ -378,8 +378,8 @@ def test_kernel_import_remote_registers_driver_and_route() -> None: def test_import_remote_requires_mutable_router() -> None: """A router without add_route() cannot make imports routable — fail clean.""" - from agent_kernel import FederationError - from agent_kernel.models import RoutePlan + from weaver_kernel import FederationError + from weaver_kernel.models import RoutePlan class _ReadOnlyRouter: """Conforms to the Router Protocol (route only); cannot accept new routes.""" diff --git a/tests/test_federation_discovery.py b/tests/test_federation_discovery.py index f54a07e..f1e2012 100644 --- a/tests/test_federation_discovery.py +++ b/tests/test_federation_discovery.py @@ -19,7 +19,7 @@ import httpx import pytest -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, DiscoveryError, @@ -34,8 +34,8 @@ sign_manifest, verify_manifest, ) -from agent_kernel.federation import build_manifest -from agent_kernel.models import CapabilityManifest +from weaver_kernel.federation import build_manifest +from weaver_kernel.models import CapabilityManifest def _build_test_manifest() -> CapabilityManifest: @@ -283,7 +283,7 @@ def handler(request: httpx.Request) -> httpx.Response: # Monkey-patch where the kernel sub-module imported `discover_peers`, # not the source module — `_federation.py` already bound a local name. - import agent_kernel.kernel._federation as kf + import weaver_kernel.kernel._federation as kf original = kf.discover_peers @@ -337,9 +337,9 @@ def test_kernel_scoped_hmac_isolation_for_imported_capability() -> None: kernel_b.import_remote(manifest, driver=InMemoryDriver(driver_id="dummy-b")) # Mint a token on kernel A. - from agent_kernel import Principal - from agent_kernel.errors import TokenInvalid - from agent_kernel.models import CapabilityRequest + from weaver_kernel import Principal + from weaver_kernel.errors import TokenInvalid + from weaver_kernel.models import CapabilityRequest principal = Principal(principal_id="alice", roles=["reader"]) req = CapabilityRequest(capability_id="metrics.read", goal="t") @@ -367,7 +367,7 @@ async def test_signed_envelope_payload_is_canonical_json() -> None: @pytest.mark.asyncio async def test_verify_manifest_rejects_malformed_envelope() -> None: """Missing keys produce a clear error.""" - from agent_kernel.errors import ManifestError + from weaver_kernel.errors import ManifestError with pytest.raises(ManifestError, match="missing required key"): verify_manifest({"signature": "x"}, secret="s") diff --git a/tests/test_firewall.py b/tests/test_firewall.py index 57eb03a..5471030 100644 --- a/tests/test_firewall.py +++ b/tests/test_firewall.py @@ -7,16 +7,16 @@ import pytest -from agent_kernel import ( +from weaver_kernel import ( BudgetConfigError, BudgetExhausted, BudgetManager, Firewall, default_token_counter, ) -from agent_kernel.firewall.budgets import Budgets -from agent_kernel.firewall.summarize import summarize -from agent_kernel.models import Handle, RawResult +from weaver_kernel.firewall.budgets import Budgets +from weaver_kernel.firewall.summarize import summarize +from weaver_kernel.models import Handle, RawResult def _handle() -> Handle: @@ -434,9 +434,9 @@ async def test_release_rejects_negative() -> None: await bm.release(-1) -def test_budget_config_error_is_agent_kernel_error() -> None: +def test_budget_config_error_is_weaver_kernel_error() -> None: """``BudgetConfigError`` is part of the public exception hierarchy.""" - from agent_kernel import AgentKernelError + from weaver_kernel import AgentKernelError assert issubclass(BudgetConfigError, AgentKernelError) diff --git a/tests/test_firewall_boundary.py b/tests/test_firewall_boundary.py index e987b4f..929e717 100644 --- a/tests/test_firewall_boundary.py +++ b/tests/test_firewall_boundary.py @@ -20,7 +20,7 @@ import pytest -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, HandleStore, @@ -33,8 +33,8 @@ StaticRouter, TraceStore, ) -from agent_kernel.firewall.transform import Firewall -from agent_kernel.models import CapabilityRequest, Handle, RawResult +from weaver_kernel.firewall.transform import Firewall +from weaver_kernel.models import CapabilityRequest, Handle, RawResult # Fake values — these strings exist nowhere except this test file. Any test # failure that prints them is loud about which boundary leaked. diff --git a/tests/test_firewall_stream.py b/tests/test_firewall_stream.py index aaffd4f..bb32722 100644 --- a/tests/test_firewall_stream.py +++ b/tests/test_firewall_stream.py @@ -18,7 +18,7 @@ import pytest -from agent_kernel import ( +from weaver_kernel import ( Budgets, Capability, CapabilityRegistry, @@ -31,8 +31,8 @@ StaticRouter, StreamingDriver, ) -from agent_kernel.drivers.base import ExecutionContext -from agent_kernel.models import CapabilityRequest, RawResult +from weaver_kernel.drivers.base import ExecutionContext +from weaver_kernel.models import CapabilityRequest, RawResult class _FakeStreamingDriver: diff --git a/tests/test_handles.py b/tests/test_handles.py index 909b2ec..a491e58 100644 --- a/tests/test_handles.py +++ b/tests/test_handles.py @@ -6,14 +6,14 @@ import pytest -from agent_kernel import ( +from weaver_kernel import ( HandleConstraintViolation, HandleExpired, HandleNotFound, HandleStore, ) -from agent_kernel.models import Handle -from agent_kernel.policy_reasons import DenialReason +from weaver_kernel.models import Handle +from weaver_kernel.policy_reasons import DenialReason @pytest.fixture() diff --git a/tests/test_kernel.py b/tests/test_kernel.py index 7a79147..5e303e3 100644 --- a/tests/test_kernel.py +++ b/tests/test_kernel.py @@ -4,7 +4,7 @@ import pytest -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, DriverError, @@ -17,7 +17,7 @@ StaticRouter, TokenExpired, ) -from agent_kernel.models import CapabilityRequest +from weaver_kernel.models import CapabilityRequest # ── Full flow: request → grant → invoke → expand → explain ───────────────────── @@ -238,7 +238,7 @@ async def test_confused_deputy_prevention(kernel: Kernel, reader_principal: Prin token = kernel.get_token(req, reader_principal, justification="") other_principal = Principal(principal_id="attacker-999", roles=["reader"]) - from agent_kernel import TokenScopeError + from weaver_kernel import TokenScopeError with pytest.raises(TokenScopeError): await kernel.invoke( @@ -254,7 +254,7 @@ async def test_confused_deputy_prevention(kernel: Kernel, reader_principal: Prin @pytest.mark.asyncio async def test_dry_run_returns_dry_run_result(kernel: Kernel, reader_principal: Principal) -> None: """dry_run=True returns DryRunResult, not Frame.""" - from agent_kernel.models import DryRunResult + from weaver_kernel.models import DryRunResult req = CapabilityRequest(capability_id="billing.list_invoices", goal="test") token = kernel.get_token(req, reader_principal, justification="") @@ -283,7 +283,7 @@ async def test_dry_run_driver_not_called( @pytest.mark.asyncio async def test_dry_run_estimated_cost(kernel: Kernel, admin_principal: Principal) -> None: """estimated_cost maps to safety class.""" - from agent_kernel.models import DryRunResult + from weaver_kernel.models import DryRunResult read_req = CapabilityRequest(capability_id="billing.list_invoices", goal="r") read_token = kernel.get_token(read_req, admin_principal, justification="") @@ -310,7 +310,7 @@ async def test_dry_run_operation_uses_args_then_capability_id( must mirror that exactly so ``DryRunResult.operation`` matches what a driver would actually receive. """ - from agent_kernel.models import DryRunResult + from weaver_kernel.models import DryRunResult req = CapabilityRequest(capability_id="billing.list_invoices", goal="t") token = kernel.get_token(req, reader_principal, justification="") @@ -346,7 +346,7 @@ async def test_dry_run_downgrades_raw_for_non_admin( (see firewall/transform.py); dry-run must downgrade too so callers cannot probe/assume raw availability they will never receive. """ - from agent_kernel.models import DryRunResult + from weaver_kernel.models import DryRunResult req = CapabilityRequest(capability_id="billing.list_invoices", goal="t") token = kernel.get_token(req, reader_principal, justification="") @@ -364,7 +364,7 @@ async def test_dry_run_downgrades_raw_for_non_admin( @pytest.mark.asyncio async def test_dry_run_preserves_raw_for_admin(kernel: Kernel, admin_principal: Principal) -> None: """Admin principals keep raw mode in dry-run (no downgrade).""" - from agent_kernel.models import DryRunResult + from weaver_kernel.models import DryRunResult req = CapabilityRequest(capability_id="billing.list_invoices", goal="t") token = kernel.get_token(req, admin_principal, justification="") @@ -384,7 +384,7 @@ async def test_dry_run_expired_token_still_raises( kernel: Kernel, reader_principal: Principal ) -> None: """Token expiry is enforced even in dry-run mode.""" - from agent_kernel import HMACTokenProvider, TokenExpired + from weaver_kernel import HMACTokenProvider, TokenExpired provider = HMACTokenProvider(secret="test-secret-do-not-use-in-prod") token = provider.issue( @@ -431,7 +431,7 @@ def test_explain_denial_write_short_justification( def test_explain_denial_capability_not_found(kernel: Kernel, reader_principal: Principal) -> None: """explain_denial raises CapabilityNotFound for unknown capability.""" - from agent_kernel import CapabilityNotFound + from weaver_kernel import CapabilityNotFound req = CapabilityRequest(capability_id="nonexistent.capability", goal="test") with pytest.raises(CapabilityNotFound): @@ -447,8 +447,8 @@ def test_explain_denial_engine_without_explain_raises( don't implement ``explain()`` raise ``AgentKernelError`` from ``Kernel.explain_denial`` rather than producing a misleading explanation. """ - from agent_kernel import AgentKernelError, PolicyDenied - from agent_kernel.models import PolicyDecision + from weaver_kernel import AgentKernelError, PolicyDenied + from weaver_kernel.models import PolicyDecision class EvaluateOnlyEngine: """Minimal engine satisfying PolicyEngine but not ExplainingPolicyEngine.""" @@ -484,7 +484,7 @@ def _kernel_with_budget( default_request: int = 4_000, ) -> Kernel: """Helper: construct a kernel wired with a BudgetManager.""" - from agent_kernel import BudgetManager + from weaver_kernel import BudgetManager router = StaticRouter( routes={ @@ -540,7 +540,7 @@ async def test_budget_manager_escalates_mode_when_remaining_under_five_percent( reader_principal: Principal, ) -> None: """When remaining drops below 5%, even ``summary`` escalates to ``handle_only``.""" - from agent_kernel import BudgetManager + from weaver_kernel import BudgetManager router = StaticRouter(routes={"billing.list_invoices": ["memory"]}) bm = BudgetManager(total_budget=1000) @@ -572,7 +572,7 @@ async def test_budget_manager_exhausted_raises_before_driver_runs( reader_principal: Principal, ) -> None: """An exhausted budget surfaces ``BudgetExhausted`` and skips the driver.""" - from agent_kernel import BudgetExhausted, BudgetManager + from weaver_kernel import BudgetExhausted, BudgetManager router = StaticRouter(routes={"billing.list_invoices": ["memory"]}) bm = BudgetManager(total_budget=100) @@ -601,7 +601,7 @@ async def test_budget_manager_releases_reservation_on_driver_failure( reader_principal: Principal, ) -> None: """When all drivers fail, the reserved tokens must return to the pool.""" - from agent_kernel import BudgetManager + from weaver_kernel import BudgetManager # Construct a kernel with a router pointing at a driver that does not exist. router = StaticRouter(routes={"billing.list_invoices": ["nope"]}) @@ -633,7 +633,7 @@ async def test_dry_run_reports_budget_remaining_when_manager_configured( reader_principal: Principal, ) -> None: """DryRunResult.budget_remaining is populated when a BudgetManager is wired.""" - from agent_kernel.models import DryRunResult + from weaver_kernel.models import DryRunResult k = _kernel_with_budget(registry, memory_driver, total_budget=10_000) req = CapabilityRequest(capability_id="billing.list_invoices", goal="t") @@ -655,8 +655,8 @@ async def test_dry_run_reflects_escalated_mode_under_budget_pressure( reader_principal: Principal, ) -> None: """Dry-run mirrors the BudgetManager escalation that a real invoke would apply.""" - from agent_kernel import BudgetManager - from agent_kernel.models import DryRunResult + from weaver_kernel import BudgetManager + from weaver_kernel.models import DryRunResult router = StaticRouter(routes={"billing.list_invoices": ["memory"]}) bm = BudgetManager(total_budget=1000) @@ -693,7 +693,7 @@ async def test_budget_manager_releases_reservation_on_firewall_failure( Without the finally block the reservation would stay locked, permanently eroding the cumulative budget on every transform failure. """ - from agent_kernel import BudgetManager, FirewallError + from weaver_kernel import BudgetManager, FirewallError class FailingFirewall: def transform(self, *args: object, **kwargs: object) -> object: @@ -772,7 +772,7 @@ async def test_kernel_without_budget_manager_behaves_identically( def test_explain_denial_surfaces_reason_code(kernel: Kernel, reader_principal: Principal) -> None: """Kernel.explain_denial forwards the engine's reason_code.""" - from agent_kernel import DenialReason + from weaver_kernel import DenialReason result = kernel.explain_denial( CapabilityRequest(capability_id="billing.update_invoice", goal="write"), @@ -788,7 +788,7 @@ def test_grant_capability_carries_intent_through_request( kernel: Kernel, reader_principal: Principal ) -> None: """A request with intent/scope should be accepted end-to-end and reach the decision trace.""" - from agent_kernel import AllowReason + from weaver_kernel import AllowReason req = CapabilityRequest( capability_id="billing.get_invoice", @@ -814,7 +814,7 @@ async def test_dry_run_policy_decision_has_trace( kernel emits a single-step ``token_verified`` trace so dry-run consumers can rely on a uniformly-shaped trace field. """ - from agent_kernel.models import DryRunResult + from weaver_kernel.models import DryRunResult token = kernel.get_token( CapabilityRequest(capability_id="billing.get_invoice", goal="read"), @@ -844,8 +844,8 @@ async def test_dry_run_with_http_driver_does_not_call_execute() -> None: """ from unittest.mock import AsyncMock, patch - from agent_kernel.drivers.http import HTTPDriver, HTTPEndpoint - from agent_kernel.models import DryRunResult + from weaver_kernel.drivers.http import HTTPDriver, HTTPEndpoint + from weaver_kernel.models import DryRunResult cap = Capability( capability_id="external.fetch_user", @@ -895,8 +895,8 @@ async def test_dry_run_with_mcp_driver_does_not_call_execute() -> None: """ from unittest.mock import AsyncMock, patch - from agent_kernel.drivers.mcp import MCPDriver - from agent_kernel.models import DryRunResult + from weaver_kernel.drivers.mcp import MCPDriver + from weaver_kernel.models import DryRunResult cap = Capability( capability_id="mcp.echo", diff --git a/tests/test_logging.py b/tests/test_logging.py index b0c1712..80bd8a0 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -17,7 +17,7 @@ import pytest -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityRegistry, HMACTokenProvider, @@ -27,7 +27,7 @@ SafetyClass, StaticRouter, ) -from agent_kernel.models import CapabilityRequest +from weaver_kernel.models import CapabilityRequest # ── Fixtures ────────────────────────────────────────────────────────────────── @@ -132,7 +132,7 @@ async def test_no_secret_material_in_logs( for the HMAC secret and token signatures.""" req = CapabilityRequest(capability_id="log.read", goal="read log") - with caplog.at_level(logging.DEBUG, logger="agent_kernel"): + with caplog.at_level(logging.DEBUG, logger="weaver_kernel"): grant = log_kernel.grant_capability(req, reader, justification="") frame = await log_kernel.invoke( grant.token, @@ -162,10 +162,10 @@ def test_grant_capability_emits_info( caplog: pytest.LogCaptureFixture, ) -> None: req = CapabilityRequest(capability_id="log.write", goal="write log") - with caplog.at_level(logging.INFO, logger="agent_kernel.kernel"): + with caplog.at_level(logging.INFO, logger="weaver_kernel.kernel"): log_kernel.grant_capability(req, writer, justification="long enough justification here") - records = [r for r in caplog.records if r.name == "agent_kernel.kernel"] + records = [r for r in caplog.records if r.name == "weaver_kernel.kernel"] assert any(r.levelno == logging.INFO for r in records), ( "Expected INFO record from grant_capability" ) @@ -189,10 +189,10 @@ async def test_invoke_emits_info( req = CapabilityRequest(capability_id="log.read", goal="read") token = log_kernel.get_token(req, reader, justification="") - with caplog.at_level(logging.INFO, logger="agent_kernel.kernel"): + with caplog.at_level(logging.INFO, logger="weaver_kernel.kernel"): await log_kernel.invoke(token, principal=reader, args={}) - kernel_records = [r for r in caplog.records if r.name == "agent_kernel.kernel"] + kernel_records = [r for r in caplog.records if r.name == "weaver_kernel.kernel"] assert any("invoke_start" in r.getMessage() for r in kernel_records), ( "Expected invoke_start log record" ) @@ -215,11 +215,11 @@ async def test_expand_emits_info( frame = await log_kernel.invoke(token, principal=reader, args={}) assert frame.handle is not None - with caplog.at_level(logging.INFO, logger="agent_kernel.kernel"): + with caplog.at_level(logging.INFO, logger="weaver_kernel.kernel"): log_kernel.expand(frame.handle, query={}, principal=reader) assert any( - "expand" in r.getMessage() for r in caplog.records if r.name == "agent_kernel.kernel" + "expand" in r.getMessage() for r in caplog.records if r.name == "weaver_kernel.kernel" ), "Expected 'expand' log record" @@ -236,11 +236,11 @@ async def test_explain_emits_info( token = log_kernel.get_token(req, reader, justification="") frame = await log_kernel.invoke(token, principal=reader, args={}) - with caplog.at_level(logging.INFO, logger="agent_kernel.kernel"): + with caplog.at_level(logging.INFO, logger="weaver_kernel.kernel"): log_kernel.explain(frame.action_id) assert any( - "explain" in r.getMessage() for r in caplog.records if r.name == "agent_kernel.kernel" + "explain" in r.getMessage() for r in caplog.records if r.name == "weaver_kernel.kernel" ), "Expected 'explain' log record" @@ -251,13 +251,13 @@ def test_request_capabilities_emits_debug( log_kernel: Kernel, caplog: pytest.LogCaptureFixture, ) -> None: - with caplog.at_level(logging.DEBUG, logger="agent_kernel.kernel"): + with caplog.at_level(logging.DEBUG, logger="weaver_kernel.kernel"): log_kernel.request_capabilities("read log") assert any( "request_capabilities" in r.getMessage() for r in caplog.records - if r.name == "agent_kernel.kernel" + if r.name == "weaver_kernel.kernel" ), "Expected 'request_capabilities' DEBUG log record" @@ -271,10 +271,10 @@ def test_policy_denial_logged_at_warning( ) -> None: """A WRITE capability denial should log WARNING with principal_id and capability_id.""" req = CapabilityRequest(capability_id="log.write", goal="write") - from agent_kernel import PolicyDenied + from weaver_kernel import PolicyDenied with ( - caplog.at_level(logging.WARNING, logger="agent_kernel.policy"), + caplog.at_level(logging.WARNING, logger="weaver_kernel.policy"), pytest.raises(PolicyDenied), ): log_kernel.grant_capability(req, reader, justification="short") @@ -282,7 +282,7 @@ def test_policy_denial_logged_at_warning( warning_records = [ r for r in caplog.records - if r.name == "agent_kernel.policy" and r.levelno == logging.WARNING + if r.name == "weaver_kernel.policy" and r.levelno == logging.WARNING ] assert warning_records, "Expected WARNING log record for policy denial" rec = warning_records[0] @@ -296,11 +296,11 @@ def test_policy_allow_logged_at_info( caplog: pytest.LogCaptureFixture, ) -> None: req = CapabilityRequest(capability_id="log.read", goal="read") - with caplog.at_level(logging.INFO, logger="agent_kernel.policy"): + with caplog.at_level(logging.INFO, logger="weaver_kernel.policy"): log_kernel.grant_capability(req, reader, justification="") info_records = [ - r for r in caplog.records if r.name == "agent_kernel.policy" and r.levelno == logging.INFO + r for r in caplog.records if r.name == "weaver_kernel.policy" and r.levelno == logging.INFO ] assert info_records, "Expected INFO log record for policy approval" rec = info_records[0] @@ -313,11 +313,13 @@ def test_policy_allow_logged_at_info( def test_token_issuance_logged_at_debug(caplog: pytest.LogCaptureFixture) -> None: provider = HMACTokenProvider(secret="test-secret-12345") - with caplog.at_level(logging.DEBUG, logger="agent_kernel.tokens"): + with caplog.at_level(logging.DEBUG, logger="weaver_kernel.tokens"): token = provider.issue("cap.x", "user-1") debug_records = [ - r for r in caplog.records if r.name == "agent_kernel.tokens" and r.levelno == logging.DEBUG + r + for r in caplog.records + if r.name == "weaver_kernel.tokens" and r.levelno == logging.DEBUG ] assert debug_records, "Expected DEBUG record for token issuance" rec = debug_records[0] @@ -334,10 +336,10 @@ def test_token_verification_failure_logged_at_warning( provider = HMACTokenProvider(secret="test-secret-12345") token = provider.issue("cap.x", "user-1", ttl_seconds=-1) - from agent_kernel import TokenExpired + from weaver_kernel import TokenExpired with ( - caplog.at_level(logging.WARNING, logger="agent_kernel.tokens"), + caplog.at_level(logging.WARNING, logger="weaver_kernel.tokens"), pytest.raises(TokenExpired), ): provider.verify( @@ -349,7 +351,7 @@ def test_token_verification_failure_logged_at_warning( warning_records = [ r for r in caplog.records - if r.name == "agent_kernel.tokens" and r.levelno == logging.WARNING + if r.name == "weaver_kernel.tokens" and r.levelno == logging.WARNING ] assert warning_records, "Expected WARNING record for token verification failure" rec = warning_records[0] @@ -362,11 +364,13 @@ def test_token_verification_failure_logged_at_warning( def test_router_resolution_logged_at_debug(caplog: pytest.LogCaptureFixture) -> None: router = StaticRouter(routes={"cap.x": ["driver-a", "driver-b"]}) - with caplog.at_level(logging.DEBUG, logger="agent_kernel.router"): + with caplog.at_level(logging.DEBUG, logger="weaver_kernel.router"): plan = router.route("cap.x") debug_records = [ - r for r in caplog.records if r.name == "agent_kernel.router" and r.levelno == logging.DEBUG + r + for r in caplog.records + if r.name == "weaver_kernel.router" and r.levelno == logging.DEBUG ] assert debug_records, "Expected DEBUG record from router" rec = debug_records[0] @@ -386,10 +390,10 @@ async def test_firewall_transform_logged_at_debug( req = CapabilityRequest(capability_id="log.read", goal="read") token = log_kernel.get_token(req, reader, justification="") - with caplog.at_level(logging.DEBUG, logger="agent_kernel.firewall.transform"): + with caplog.at_level(logging.DEBUG, logger="weaver_kernel.firewall.transform"): await log_kernel.invoke(token, principal=reader, args={}) - fw_records = [r for r in caplog.records if r.name == "agent_kernel.firewall.transform"] + fw_records = [r for r in caplog.records if r.name == "weaver_kernel.firewall.transform"] assert fw_records, "Expected DEBUG records from firewall transform" modes = {r.getMessage() for r in fw_records} assert any("firewall_transform" in m or "firewall_redaction" in m for m in modes) @@ -406,13 +410,13 @@ async def test_destructive_grant_invoke_logging( ) -> None: """Admin grants DESTRUCTIVE capability; both grant and invoke emit INFO logs.""" req = CapabilityRequest(capability_id="log.destroy", goal="destroy log") - with caplog.at_level(logging.INFO, logger="agent_kernel"): + with caplog.at_level(logging.INFO, logger="weaver_kernel"): grant = log_kernel.grant_capability( req, admin, justification="destroying old logs for compliance cleanup" ) await log_kernel.invoke(grant.token, principal=admin, args={}) - kernel_records = [r for r in caplog.records if r.name == "agent_kernel.kernel"] + kernel_records = [r for r in caplog.records if r.name == "weaver_kernel.kernel"] assert any("grant_capability" in r.getMessage() for r in kernel_records) grant_rec = next(r for r in kernel_records if "grant_capability" in r.getMessage()) assert grant_rec.capability_id == "log.destroy" # type: ignore[attr-defined] @@ -426,11 +430,11 @@ def test_destructive_denial_logging( caplog: pytest.LogCaptureFixture, ) -> None: """A reader denied DESTRUCTIVE capability emits policy_denied WARNING.""" - from agent_kernel import PolicyDenied + from weaver_kernel import PolicyDenied req = CapabilityRequest(capability_id="log.destroy", goal="destroy log") with ( - caplog.at_level(logging.WARNING, logger="agent_kernel.policy"), + caplog.at_level(logging.WARNING, logger="weaver_kernel.policy"), pytest.raises(PolicyDenied), ): log_kernel.grant_capability(req, reader, justification="short") @@ -438,7 +442,7 @@ def test_destructive_denial_logging( warning_records = [ r for r in caplog.records - if r.name == "agent_kernel.policy" and r.levelno == logging.WARNING + if r.name == "weaver_kernel.policy" and r.levelno == logging.WARNING ] assert warning_records, "Expected WARNING log for DESTRUCTIVE denial" rec = warning_records[0] @@ -455,11 +459,11 @@ async def test_no_debug_noise_at_info_level( reader: Principal, caplog: pytest.LogCaptureFixture, ) -> None: - """At INFO level, no DEBUG records should appear from agent_kernel modules.""" + """At INFO level, no DEBUG records should appear from weaver_kernel modules.""" req = CapabilityRequest(capability_id="log.read", goal="read") token = log_kernel.get_token(req, reader, justification="") - with caplog.at_level(logging.INFO, logger="agent_kernel"): + with caplog.at_level(logging.INFO, logger="weaver_kernel"): await log_kernel.invoke(token, principal=reader, args={}) debug_records = [r for r in caplog.records if r.levelno == logging.DEBUG] diff --git a/tests/test_mcp_driver.py b/tests/test_mcp_driver.py index 0f293a8..53097d3 100644 --- a/tests/test_mcp_driver.py +++ b/tests/test_mcp_driver.py @@ -10,7 +10,7 @@ import pytest from mcp.types import CallToolResult, ListToolsResult, TextContent, Tool -from agent_kernel import ( +from weaver_kernel import ( CapabilityRegistry, CapabilityRequest, DriverError, @@ -71,14 +71,14 @@ async def _factory() -> AsyncIterator[_FakeSession]: def test_from_stdio_missing_dependency_raises_helpful_import_error() -> None: - with patch("agent_kernel.drivers.mcp_support.importlib.import_module") as import_module: + with patch("weaver_kernel.drivers.mcp_support.importlib.import_module") as import_module: import_module.side_effect = ModuleNotFoundError("No module named 'mcp'") with pytest.raises(ImportError, match=r"weaver-kernel\[mcp\]"): MCPDriver.from_stdio("python", ["server.py"]) def test_from_http_missing_dependency_raises_helpful_import_error() -> None: - with patch("agent_kernel.drivers.mcp_support.importlib.import_module") as import_module: + with patch("weaver_kernel.drivers.mcp_support.importlib.import_module") as import_module: import_module.side_effect = ModuleNotFoundError("No module named 'mcp'") with pytest.raises(ImportError, match=r"weaver-kernel\[mcp\]"): MCPDriver.from_http("http://localhost:8080/mcp") @@ -88,16 +88,16 @@ def test_load_mcp_error_returns_type_when_mcp_available() -> None: """_load_mcp_error resolves the real McpError type when mcp is installed.""" from mcp.shared.exceptions import McpError - from agent_kernel.drivers.mcp import _load_mcp_error + from weaver_kernel.drivers.mcp import _load_mcp_error assert _load_mcp_error() is McpError def test_load_mcp_error_returns_none_when_mcp_unavailable() -> None: """_load_mcp_error returns None when the optional mcp dep is missing (issue #87).""" - from agent_kernel.drivers.mcp import _load_mcp_error + from weaver_kernel.drivers.mcp import _load_mcp_error - with patch("agent_kernel.drivers.mcp.importlib.import_module") as import_module: + with patch("weaver_kernel.drivers.mcp.importlib.import_module") as import_module: import_module.side_effect = ImportError("No module named 'mcp'") assert _load_mcp_error() is None @@ -110,10 +110,10 @@ def test_load_mcp_error_returns_none_when_attribute_missing() -> None: """ from types import ModuleType - from agent_kernel.drivers.mcp import _load_mcp_error + from weaver_kernel.drivers.mcp import _load_mcp_error empty_module = ModuleType("mcp.shared.exceptions") - with patch("agent_kernel.drivers.mcp.importlib.import_module") as import_module: + with patch("weaver_kernel.drivers.mcp.importlib.import_module") as import_module: import_module.return_value = empty_module assert _load_mcp_error() is None @@ -160,7 +160,7 @@ async def test_execute_calls_tool_and_applies_constraints_defaults() -> None: transport="stdio", ) - from agent_kernel.drivers.base import ExecutionContext + from weaver_kernel.drivers.base import ExecutionContext ctx = ExecutionContext( capability_id="fs.list_files", @@ -194,7 +194,7 @@ async def test_execute_prefers_structured_content_when_available() -> None: transport="stdio", ) - from agent_kernel.drivers.base import ExecutionContext + from weaver_kernel.drivers.base import ExecutionContext ctx = ExecutionContext(capability_id="math.sum", principal_id="u1") result = await driver.execute(ctx) @@ -220,7 +220,7 @@ async def test_execute_raises_driver_error_on_mcp_is_error() -> None: transport="stdio", ) - from agent_kernel.drivers.base import ExecutionContext + from weaver_kernel.drivers.base import ExecutionContext ctx = ExecutionContext(capability_id="secrets.read", principal_id="u1") with pytest.raises(DriverError, match="permission denied"): @@ -242,7 +242,7 @@ async def test_http_transport_retries_after_connection_drop() -> None: max_http_retries=1, ) - from agent_kernel.drivers.base import ExecutionContext + from weaver_kernel.drivers.base import ExecutionContext ctx = ExecutionContext(capability_id="echo", principal_id="u1") result = await driver.execute(ctx) @@ -319,7 +319,7 @@ async def in_memory_factory() -> AsyncIterator[ClientSession]: assert add_cap.impl is not None assert add_cap.impl.operation == "add" - from agent_kernel.drivers.base import ExecutionContext + from weaver_kernel.drivers.base import ExecutionContext ctx = ExecutionContext( capability_id="math.add", diff --git a/tests/test_models.py b/tests/test_models.py index bff18ec..0896c8d 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -4,9 +4,9 @@ import datetime -from agent_kernel.enums import SafetyClass, SensitivityTag -from agent_kernel.firewall.budgets import Budgets -from agent_kernel.models import ( +from weaver_kernel.enums import SafetyClass, SensitivityTag +from weaver_kernel.firewall.budgets import Budgets +from weaver_kernel.models import ( ActionTrace, Capability, CapabilityRequest, @@ -18,7 +18,7 @@ RawResult, RoutePlan, ) -from agent_kernel.tokens import CapabilityToken +from weaver_kernel.tokens import CapabilityToken def test_capability_construction() -> None: @@ -190,7 +190,7 @@ def test_capability_request_scope_is_independent_per_instance() -> None: def test_policy_decision_trace_defaults() -> None: - from agent_kernel.models import PolicyDecisionTrace + from weaver_kernel.models import PolicyDecisionTrace trace = PolicyDecisionTrace( engine="X", @@ -206,7 +206,7 @@ def test_policy_decision_trace_defaults() -> None: def test_policy_trace_step_uses_slots() -> None: """PolicyTraceStep is a slotted dataclass; new attributes must not be settable.""" - from agent_kernel.models import PolicyTraceStep + from weaver_kernel.models import PolicyTraceStep step = PolicyTraceStep(name="x", outcome="allowed") import pytest @@ -228,7 +228,7 @@ def test_policy_decision_trace_is_optional_on_decision() -> None: def test_policy_decision_reason_code_round_trip() -> None: - from agent_kernel import DenialReason + from weaver_kernel import DenialReason dec = PolicyDecision(allowed=False, reason="nope", reason_code=DenialReason.MISSING_ROLE) assert dec.reason_code == DenialReason.MISSING_ROLE @@ -237,7 +237,7 @@ def test_policy_decision_reason_code_round_trip() -> None: def test_failed_condition_default_reason_code_is_none() -> None: - from agent_kernel.models import FailedCondition + from weaver_kernel.models import FailedCondition fc = FailedCondition(condition="x", required=1, actual=0, suggestion="bump x") assert fc.reason_code is None diff --git a/tests/test_otel.py b/tests/test_otel.py index b8bcd1e..a965213 100644 --- a/tests/test_otel.py +++ b/tests/test_otel.py @@ -1,4 +1,4 @@ -"""Tests for OpenTelemetry instrumentation (:func:`agent_kernel.instrument_kernel`). +"""Tests for OpenTelemetry instrumentation (:func:`weaver_kernel.instrument_kernel`). These tests use the OpenTelemetry SDK's ``InMemorySpanExporter`` and ``InMemoryMetricReader`` so we don't need a running collector. They @@ -16,7 +16,7 @@ import pytest -from agent_kernel import ( +from weaver_kernel import ( OTEL_AVAILABLE, Capability, CapabilityRegistry, @@ -28,9 +28,9 @@ StaticRouter, instrument_kernel, ) -from agent_kernel.drivers.base import ExecutionContext -from agent_kernel.models import CapabilityRequest -from agent_kernel.otel import reset_instrumentation +from weaver_kernel.drivers.base import ExecutionContext +from weaver_kernel.models import CapabilityRequest +from weaver_kernel.otel import reset_instrumentation if not OTEL_AVAILABLE: # pragma: no cover - skipped without the [otel] extra pytest.skip( @@ -108,7 +108,7 @@ async def test_instrumented_invoke_emits_span( InMemorySpanExporter, InMemoryMetricReader, TracerProvider, MeterProvider ], ) -> None: - """`Kernel.invoke()` produces one ``agent_kernel.invoke`` span.""" + """`Kernel.invoke()` produces one ``weaver_kernel.invoke`` span.""" spans, _, tp, mp = otel_exporters kernel, principal = _build_kernel() instrument_kernel(kernel, tracer_provider=tp, meter_provider=mp) @@ -118,13 +118,13 @@ async def test_instrumented_invoke_emits_span( await kernel.invoke(token, principal=principal, args={}) finished = spans.get_finished_spans() - invoke_spans = [s for s in finished if s.name == "agent_kernel.invoke"] + invoke_spans = [s for s in finished if s.name == "weaver_kernel.invoke"] assert len(invoke_spans) == 1, [s.name for s in finished] attrs = invoke_spans[0].attributes or {} - assert attrs.get("agent_kernel.principal_id") == "otel-user" - assert attrs.get("agent_kernel.capability_id") == "metrics.read" - assert attrs.get("agent_kernel.response_mode") == "summary" - assert attrs.get("agent_kernel.dry_run") is False + assert attrs.get("weaver_kernel.principal_id") == "otel-user" + assert attrs.get("weaver_kernel.capability_id") == "metrics.read" + assert attrs.get("weaver_kernel.response_mode") == "summary" + assert attrs.get("weaver_kernel.dry_run") is False @pytest.mark.asyncio @@ -133,7 +133,7 @@ async def test_uninstrumented_invoke_emits_no_span( InMemorySpanExporter, InMemoryMetricReader, TracerProvider, MeterProvider ], ) -> None: - """A kernel that was never wrapped emits zero ``agent_kernel.*`` spans.""" + """A kernel that was never wrapped emits zero ``weaver_kernel.*`` spans.""" spans, _, _, _ = otel_exporters kernel, principal = _build_kernel() # Deliberately do NOT call instrument_kernel. @@ -143,7 +143,7 @@ async def test_uninstrumented_invoke_emits_no_span( await kernel.invoke(token, principal=principal, args={}) finished = spans.get_finished_spans() - invoke_spans = [s for s in finished if s.name.startswith("agent_kernel.")] + invoke_spans = [s for s in finished if s.name.startswith("weaver_kernel.")] assert invoke_spans == [] @@ -169,13 +169,13 @@ def test_instrumented_grant_records_denial( reader = Principal(principal_id="reader-only", roles=["reader"]) req = CapabilityRequest(capability_id="metrics.write", goal="t") - from agent_kernel.errors import PolicyDenied + from weaver_kernel.errors import PolicyDenied with pytest.raises(PolicyDenied): kernel.grant_capability(req, reader, justification="too short") finished = spans.get_finished_spans() - grant_spans = [s for s in finished if s.name == "agent_kernel.grant"] + grant_spans = [s for s in finished if s.name == "weaver_kernel.grant"] assert len(grant_spans) == 1 assert grant_spans[0].status.status_code.name == "ERROR" diff --git a/tests/test_policy.py b/tests/test_policy.py index 977380e..8cdaed6 100644 --- a/tests/test_policy.py +++ b/tests/test_policy.py @@ -8,7 +8,7 @@ import pytest -from agent_kernel import ( +from weaver_kernel import ( AgentKernelError, Capability, DeclarativePolicyEngine, @@ -19,8 +19,8 @@ SafetyClass, SensitivityTag, ) -from agent_kernel.models import CapabilityRequest -from agent_kernel.policy import RateLimiter +from weaver_kernel.models import CapabilityRequest +from weaver_kernel.policy import RateLimiter def _req(cap_id: str, **constraints: object) -> CapabilityRequest: @@ -293,7 +293,7 @@ def test_memory_read_project_scope_allowed(engine: DefaultPolicyEngine) -> None: def test_memory_read_sensitive_denied_without_role(engine: DefaultPolicyEngine) -> None: """Reading sensitive-scoped memory requires memory_reader_sensitive.""" - from agent_kernel.policy_reasons import DenialReason + from weaver_kernel.policy_reasons import DenialReason p = Principal(principal_id="u1", roles=["reader"]) cap = _cap("memory.read", SafetyClass.READ, SensitivityTag.MEMORY) @@ -319,7 +319,7 @@ def test_memory_read_sensitive_allowed_with_admin(engine: DefaultPolicyEngine) - def test_memory_write_denied_without_writer_role(engine: DefaultPolicyEngine) -> None: """memory.write requires the memory_writer role even when SafetyClass=WRITE would otherwise be satisfied by a generic writer.""" - from agent_kernel.policy_reasons import DenialReason + from weaver_kernel.policy_reasons import DenialReason p = Principal(principal_id="u1", roles=["writer"]) cap = _cap("memory.write", SafetyClass.WRITE, SensitivityTag.MEMORY) @@ -349,7 +349,7 @@ def test_memory_write_allowed_with_admin(engine: DefaultPolicyEngine) -> None: def test_memory_destructive_requires_writer(engine: DefaultPolicyEngine) -> None: """DESTRUCTIVE memory (e.g. memory.forget) is treated as write-class.""" - from agent_kernel.policy_reasons import DenialReason + from weaver_kernel.policy_reasons import DenialReason p = Principal(principal_id="u1", roles=["admin"]) # admin passes the DESTRUCTIVE safety_class. The MEMORY branch then needs @@ -370,7 +370,7 @@ def test_memory_destructive_requires_writer(engine: DefaultPolicyEngine) -> None def test_memory_explain_lists_failed_conditions() -> None: """explain() lists the memory denial alongside the FailedCondition reason_code.""" - from agent_kernel.policy_reasons import DenialReason + from weaver_kernel.policy_reasons import DenialReason eng = DefaultPolicyEngine() p = Principal(principal_id="u1", roles=["reader"]) @@ -1379,7 +1379,7 @@ def fake_import(name: str, *args: object, **kwargs: object) -> object: # ═══════════════════════════════════════════════════════════════════════════════ -from agent_kernel import AllowReason, DenialReason # noqa: E402 +from weaver_kernel import AllowReason, DenialReason # noqa: E402 class TestDefaultEngineReasonCodes: diff --git a/tests/test_public_api.py b/tests/test_public_api.py index d6c84a1..e68e5a1 100644 --- a/tests/test_public_api.py +++ b/tests/test_public_api.py @@ -1,8 +1,8 @@ """Public API consistency tests. -Pins the contract that the ``agent_kernel`` module docstring's ``Errors::`` +Pins the contract that the ``weaver_kernel`` module docstring's ``Errors::`` block stays in sync with the error classes actually exported via ``__all__``. -A newcomer who reads ``help(agent_kernel)`` should see every public error +A newcomer who reads ``help(weaver_kernel)`` should see every public error class, not a stale subset (issue #91). """ @@ -10,15 +10,15 @@ import re -import agent_kernel -from agent_kernel.errors import AgentKernelError +import weaver_kernel +from weaver_kernel.errors import AgentKernelError def _exported_error_names() -> list[str]: """Return every name in ``__all__`` that is an exported error class.""" names: list[str] = [] - for name in agent_kernel.__all__: - obj = getattr(agent_kernel, name) + for name in weaver_kernel.__all__: + obj = getattr(weaver_kernel, name) if isinstance(obj, type) and issubclass(obj, AgentKernelError): names.append(name) return names @@ -26,7 +26,7 @@ def _exported_error_names() -> list[str]: def test_all_exported_errors_listed_in_module_docstring() -> None: """Every exported error class appears in the module docstring.""" - doc = agent_kernel.__doc__ or "" + doc = weaver_kernel.__doc__ or "" # Use word-boundary matching so a shorter name (e.g. ``ManifestError``) is # not falsely considered present merely because it is a contiguous # substring of a longer listed name (e.g. ``ManifestSomethingError``). @@ -34,7 +34,7 @@ def test_all_exported_errors_listed_in_module_docstring() -> None: name for name in _exported_error_names() if not re.search(rf"\b{re.escape(name)}\b", doc) ] assert missing == [], ( - f"Error classes exported in __all__ but absent from the agent_kernel " + f"Error classes exported in __all__ but absent from the weaver_kernel " f"module docstring's 'Errors::' block: {missing}. Add them in " - f"src/agent_kernel/__init__.py so help(agent_kernel) stays accurate." + f"src/weaver_kernel/__init__.py so help(weaver_kernel) stays accurate." ) diff --git a/tests/test_redaction.py b/tests/test_redaction.py index 8699cbb..54f7933 100644 --- a/tests/test_redaction.py +++ b/tests/test_redaction.py @@ -4,7 +4,7 @@ import pytest -from agent_kernel.firewall.redaction import ( +from weaver_kernel.firewall.redaction import ( _API_KEY_RE, _BEARER_RE, _CARD_RE, diff --git a/tests/test_registry.py b/tests/test_registry.py index 01989f3..4cd97b3 100644 --- a/tests/test_registry.py +++ b/tests/test_registry.py @@ -4,7 +4,7 @@ import pytest -from agent_kernel import ( +from weaver_kernel import ( Capability, CapabilityAlreadyRegistered, CapabilityNotFound, @@ -66,7 +66,7 @@ def test_search_basic(registry: CapabilityRegistry) -> None: def test_search_returns_capabilityrequest(registry: CapabilityRegistry) -> None: - from agent_kernel.models import CapabilityRequest + from weaver_kernel.models import CapabilityRequest results = registry.search("billing invoice") assert all(isinstance(r, CapabilityRequest) for r in results) @@ -155,7 +155,7 @@ def test_list_namespace_exact_match_is_included() -> None: def test_list_namespace_unknown_prefix_raises() -> None: - from agent_kernel import NamespaceNotFound + from weaver_kernel import NamespaceNotFound reg = CapabilityRegistry() reg.register(_make_cap("billing.invoices.list")) @@ -262,7 +262,7 @@ def deep() -> list[Capability]: def test_namespace_loader_out_of_namespace_capability_raises() -> None: """A loader returning a capability outside its namespace is a contract error.""" - from agent_kernel import FederationError + from weaver_kernel import FederationError def loader() -> list[Capability]: return [_make_cap("billing.invoices.list"), _make_cap("crm.contacts.list")] @@ -277,7 +277,7 @@ def loader() -> list[Capability]: def test_namespace_loader_failure_stays_retryable() -> None: """A failed load resets the loaded flag so a later access can retry.""" - from agent_kernel import FederationError + from weaver_kernel import FederationError calls: list[int] = [] diff --git a/tests/test_router.py b/tests/test_router.py index 1777127..20c5860 100644 --- a/tests/test_router.py +++ b/tests/test_router.py @@ -2,7 +2,7 @@ from __future__ import annotations -from agent_kernel import StaticRouter +from weaver_kernel import StaticRouter def test_explicit_route() -> None: diff --git a/tests/test_tokens.py b/tests/test_tokens.py index 9da0cc8..ac47a68 100644 --- a/tests/test_tokens.py +++ b/tests/test_tokens.py @@ -4,7 +4,7 @@ import pytest -from agent_kernel import ( +from weaver_kernel import ( HMACTokenProvider, TokenExpired, TokenInvalid, @@ -71,7 +71,7 @@ def test_token_with_constraints(provider: HMACTokenProvider) -> None: def test_token_serialization_roundtrip(provider: HMACTokenProvider) -> None: token = provider.issue("cap.x", "user-1", constraints={"foo": "bar"}) d = token.to_dict() - from agent_kernel.tokens import CapabilityToken + from weaver_kernel.tokens import CapabilityToken restored = CapabilityToken.from_dict(d) assert restored.token_id == token.token_id @@ -84,7 +84,7 @@ def test_tamper_constraints_invalidates_token(provider: HMACTokenProvider) -> No token = provider.issue("cap.x", "user-1", constraints={"max_rows": 10}) d = token.to_dict() d["constraints"]["max_rows"] = 9999 # tamper - from agent_kernel.tokens import CapabilityToken + from weaver_kernel.tokens import CapabilityToken tampered = CapabilityToken.from_dict(d) with pytest.raises(TokenInvalid): @@ -95,16 +95,16 @@ def test_dev_secret_warning(caplog: pytest.LogCaptureFixture) -> None: """A provider with no secret should generate a warning.""" import logging - import agent_kernel.tokens as tok_mod + import weaver_kernel.tokens as tok_mod # Save and restore _DEV_SECRET to avoid leaking state to other tests original = tok_mod._DEV_SECRET try: tok_mod._DEV_SECRET = None provider_no_secret = HMACTokenProvider(secret=None) - with caplog.at_level(logging.WARNING, logger="agent_kernel.tokens"): + with caplog.at_level(logging.WARNING, logger="weaver_kernel.tokens"): token = provider_no_secret.issue("cap.x", "user-1") - assert "AGENT_KERNEL_SECRET" in caplog.text + assert "WEAVER_KERNEL_SECRET" in caplog.text assert token.signature != "" finally: tok_mod._DEV_SECRET = original diff --git a/tests/test_trace.py b/tests/test_trace.py index 9a4b498..2c8a469 100644 --- a/tests/test_trace.py +++ b/tests/test_trace.py @@ -6,9 +6,9 @@ import pytest -from agent_kernel import TraceStore -from agent_kernel.errors import AgentKernelError -from agent_kernel.models import ActionTrace +from weaver_kernel import TraceStore +from weaver_kernel.errors import AgentKernelError +from weaver_kernel.models import ActionTrace def _trace(action_id: str = "act-1") -> ActionTrace: diff --git a/tests/test_version.py b/tests/test_version.py index 8257da8..8329d20 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -1,10 +1,10 @@ """Version-consistency tests. -Pins the contract that ``agent_kernel.__version__`` is derived from the +Pins the contract that ``weaver_kernel.__version__`` is derived from the installed distribution metadata rather than a hand-maintained literal, so it can never drift from ``pyproject.toml`` again (issue #85). The PyPI distribution name is ``weaver-kernel``, distinct from the import name -``agent_kernel``. +``weaver_kernel``. """ from __future__ import annotations @@ -15,7 +15,7 @@ import pytest -import agent_kernel +import weaver_kernel _PYPROJECT = Path(__file__).resolve().parent.parent / "pyproject.toml" @@ -44,7 +44,7 @@ def test_version_matches_distribution_metadata() -> None: Pins that ``__version__`` is *derived from* dist metadata (not a literal); cross-checking against ``pyproject.toml`` is done separately below. """ - assert agent_kernel.__version__ == pkg_version("weaver-kernel") + assert weaver_kernel.__version__ == pkg_version("weaver-kernel") def test_version_matches_pyproject_declaration() -> None: @@ -58,9 +58,9 @@ def test_version_matches_pyproject_declaration() -> None: declared = _declared_pyproject_version() if declared is None: pytest.skip("pyproject.toml not present (installed without source tree)") - assert agent_kernel.__version__ == declared + assert weaver_kernel.__version__ == declared def test_version_is_exported() -> None: """``__version__`` stays part of the public API surface.""" - assert "__version__" in agent_kernel.__all__ + assert "__version__" in weaver_kernel.__all__