agentforge-core 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/PKG-INFO +1 -1
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/pyproject.toml +1 -1
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/__init__.py +9 -1
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/config/schema.py +33 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/__init__.py +3 -0
- agentforge_core-0.4.0/src/agentforge_core/contracts/identity.py +88 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/testing/__init__.py +2 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/testing/conformance.py +72 -0
- agentforge_core-0.4.0/src/agentforge_core/values/auth.py +36 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/values/retrieval.py +8 -0
- agentforge_core-0.3.0/src/agentforge_core/values/auth.py +0 -20
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/.gitignore +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/LICENSE +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/README.md +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/_bm25.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/config/__init__.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/config/app_sections.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/config/loader.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/config/module_schemas.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/auth.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/chat.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/embedding.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/evaluator.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/finding.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/graph_store.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/guardrails.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/llm.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/memory.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/migrator.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/protocol_bridge.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/renderer.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/reranker.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/strategy.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/task.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/tool.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/vector_store.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/migrations/__init__.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/migrations/discover.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/migrations/template.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/observability/__init__.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/observability/tracing.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/production/__init__.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/production/budget.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/production/exceptions.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/production/fallback.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/production/log_filter.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/production/log_format.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/production/run_context.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/py.typed +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/resolver/__init__.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/resolver/discover.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/resolver/resolve.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/values/__init__.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/values/chat.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/values/claim.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/values/graph.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/values/guardrails.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/values/manifest.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/values/messages.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/values/module.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/values/pipeline.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/values/state.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/values/vector.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/conftest.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/integration/test_app_sections_real_discovery.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/integration/test_config_imports_cli.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/.gitkeep +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_auth_values.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_bm25.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_budget.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_capability_extensions.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_chat_conformance.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_chat_values.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_claim.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_config_app_passthrough.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_config_app_sections.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_config_chat.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_config_feat012.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_config_imports.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_config_module_schemas.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_config_pipeline.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_config_retrieval.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_contracts_auth.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_contracts_chat.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_contracts_evaluator.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_contracts_finding.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_contracts_llm.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_contracts_memory.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_contracts_strategy.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_contracts_task.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_contracts_tool.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_embedding_client.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_exceptions.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_fallback_chain.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_graph_store_contract.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_guardrails_config.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_guardrails_contracts.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_log_filter.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_log_format.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_messages.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_migrations.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_pipeline_values.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_provider_errors.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_reranker_contract.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_resolver.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_resolver_discovery.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_run_context.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_state.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_strategy_conformance.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_strategy_stream_default.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_task_conformance.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_values_graph.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_values_vector.py +0 -0
- {agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/unit/test_vector_store_contract.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentforge-core
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: AgentForge core — stable contracts (ABCs, value types) for the agentic framework
|
|
5
5
|
Project-URL: Homepage, https://github.com/Scaffoldic/agentforge-py
|
|
6
6
|
Project-URL: Repository, https://github.com/Scaffoldic/agentforge-py
|
|
@@ -11,6 +11,11 @@ the public surface for granular imports.
|
|
|
11
11
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
|
+
# Version is sourced from the installed distribution metadata so it can
|
|
15
|
+
# never drift from pyproject.toml (bug-024).
|
|
16
|
+
from importlib.metadata import PackageNotFoundError as _PkgNotFound
|
|
17
|
+
from importlib.metadata import version as _dist_version
|
|
18
|
+
|
|
14
19
|
from agentforge_core.config import (
|
|
15
20
|
AgentConfig,
|
|
16
21
|
AgentForgeConfig,
|
|
@@ -126,7 +131,10 @@ from agentforge_core.values import (
|
|
|
126
131
|
VectorMatch,
|
|
127
132
|
)
|
|
128
133
|
|
|
129
|
-
|
|
134
|
+
try:
|
|
135
|
+
__version__ = _dist_version("agentforge-core")
|
|
136
|
+
except _PkgNotFound: # pragma: no cover - source tree without installed metadata
|
|
137
|
+
__version__ = "0.0.0+unknown"
|
|
130
138
|
|
|
131
139
|
__all__ = [
|
|
132
140
|
"OBSERVABILITY_SCOPE_NAME",
|
|
@@ -164,6 +164,10 @@ class GraphExpansionConfig(BaseModel):
|
|
|
164
164
|
"""Edge-type filter. YAML lists deserialize to `list[str]`;
|
|
165
165
|
converted to a tuple by `build_retriever_from_config` before
|
|
166
166
|
constructing the `GraphExpansion` value."""
|
|
167
|
+
direction: Literal["out", "in", "any"] = "any"
|
|
168
|
+
"""Edge direction to follow during expansion (enh-005): `out`
|
|
169
|
+
(callees / what-X-cites), `in` (callers / who-cites-X), or `any`
|
|
170
|
+
(default — the original undirected `traverse` behaviour)."""
|
|
167
171
|
text_property: str = "text"
|
|
168
172
|
decay: float = Field(default=0.5, gt=0.0, le=1.0)
|
|
169
173
|
|
|
@@ -456,6 +460,34 @@ class OutputConfig(BaseModel):
|
|
|
456
460
|
thresholds: dict[str, list[str]] = Field(default_factory=dict)
|
|
457
461
|
|
|
458
462
|
|
|
463
|
+
class IdentityConfig(BaseModel):
|
|
464
|
+
"""`governance.identity:` — the agent's identity (feat-029).
|
|
465
|
+
|
|
466
|
+
`provider` resolves against the `identity_providers` entry-point
|
|
467
|
+
category; `name` / `owner` / `attributes` describe the principal the
|
|
468
|
+
framework issues for this agent; `config` is provider-specific (issuer
|
|
469
|
+
URL, trust domain, role_arn, …)."""
|
|
470
|
+
|
|
471
|
+
model_config = ConfigDict(strict=True, extra="forbid")
|
|
472
|
+
|
|
473
|
+
provider: str = "local"
|
|
474
|
+
name: str | None = None
|
|
475
|
+
owner: str | None = None
|
|
476
|
+
attributes: dict[str, str] = Field(default_factory=dict)
|
|
477
|
+
config: dict[str, Any] = Field(default_factory=dict)
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
class GovernanceConfig(BaseModel):
|
|
481
|
+
"""`governance:` — the governance spine (feat-029+).
|
|
482
|
+
|
|
483
|
+
Identity ships first; `registry` / `policy` / `audit` sub-blocks are
|
|
484
|
+
added as those pillars land."""
|
|
485
|
+
|
|
486
|
+
model_config = ConfigDict(strict=True, extra="forbid")
|
|
487
|
+
|
|
488
|
+
identity: IdentityConfig | None = None
|
|
489
|
+
|
|
490
|
+
|
|
459
491
|
class AgentForgeConfig(BaseModel):
|
|
460
492
|
"""Root model — `agentforge.yaml` shape.
|
|
461
493
|
|
|
@@ -472,6 +504,7 @@ class AgentForgeConfig(BaseModel):
|
|
|
472
504
|
logging: LoggingConfig = Field(default_factory=LoggingConfig)
|
|
473
505
|
output: OutputConfig = Field(default_factory=OutputConfig)
|
|
474
506
|
guardrail_policy: GuardrailPolicy = Field(default_factory=GuardrailPolicy)
|
|
507
|
+
governance: GovernanceConfig = Field(default_factory=GovernanceConfig)
|
|
475
508
|
app: dict[str, Any] = Field(default_factory=dict)
|
|
476
509
|
"""Reserved namespace for **application** config (enh-002, feat-026
|
|
477
510
|
Phase 1). The framework accepts this subtree but does not interpret
|
|
@@ -13,6 +13,7 @@ from agentforge_core.contracts.embedding import EmbeddingClient
|
|
|
13
13
|
from agentforge_core.contracts.evaluator import EvalResult, Evaluator
|
|
14
14
|
from agentforge_core.contracts.finding import Finding
|
|
15
15
|
from agentforge_core.contracts.graph_store import GraphStore
|
|
16
|
+
from agentforge_core.contracts.identity import IdentityError, IdentityProvider
|
|
16
17
|
from agentforge_core.contracts.llm import LLMClient
|
|
17
18
|
from agentforge_core.contracts.memory import MemoryStore
|
|
18
19
|
from agentforge_core.contracts.migrator import (
|
|
@@ -38,6 +39,8 @@ __all__ = [
|
|
|
38
39
|
"FindingRenderer",
|
|
39
40
|
"GraphStore",
|
|
40
41
|
"HistoryTruncationStrategy",
|
|
42
|
+
"IdentityError",
|
|
43
|
+
"IdentityProvider",
|
|
41
44
|
"LLMClient",
|
|
42
45
|
"MemoryStore",
|
|
43
46
|
"Migration",
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""`IdentityProvider` — locked governance-identity contract (feat-029).
|
|
2
|
+
|
|
3
|
+
Every agent (and the tools / services it speaks to) gets a stable, portable
|
|
4
|
+
`Principal` so every action has a name. A provider:
|
|
5
|
+
|
|
6
|
+
- **issues** a principal for an agent from its declared name + owner,
|
|
7
|
+
- **resolves** a principal by id,
|
|
8
|
+
- **verifies** an inbound credential into a principal (prove who's calling),
|
|
9
|
+
- mints an outbound **credential** for a principal (prove who we are),
|
|
10
|
+
- **rotates** a principal's credential.
|
|
11
|
+
|
|
12
|
+
Per ADR-0007 the methods on this ABC are locked once the feature ships;
|
|
13
|
+
adding a method is a major-version bump. Optional behaviour goes behind
|
|
14
|
+
`capabilities()` / `supports()`.
|
|
15
|
+
|
|
16
|
+
The contract is vendor-neutral by construction: the `local` driver is
|
|
17
|
+
self-contained and offline (deterministic, no network); OIDC / SPIFFE /
|
|
18
|
+
cloud-IAM drivers map onto the same surface without the framework taking a
|
|
19
|
+
hard dependency on any of them.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from abc import ABC, abstractmethod
|
|
25
|
+
from collections.abc import Mapping
|
|
26
|
+
|
|
27
|
+
from agentforge_core.values.auth import Principal
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class IdentityError(ValueError):
|
|
31
|
+
"""Raised when a credential cannot be verified into a principal, or a
|
|
32
|
+
principal cannot be resolved for an operation that requires one."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class IdentityProvider(ABC):
|
|
36
|
+
"""Issues, resolves, verifies, and rotates `Principal` identities."""
|
|
37
|
+
|
|
38
|
+
@abstractmethod
|
|
39
|
+
async def issue(
|
|
40
|
+
self,
|
|
41
|
+
*,
|
|
42
|
+
name: str,
|
|
43
|
+
owner: str,
|
|
44
|
+
attributes: Mapping[str, str] | None = None,
|
|
45
|
+
) -> Principal:
|
|
46
|
+
"""Mint (or return the existing) principal for an agent.
|
|
47
|
+
|
|
48
|
+
`name` is the agent's logical name; `owner` the accountable team /
|
|
49
|
+
human; `attributes` free-form string tags (env, region, …). The
|
|
50
|
+
returned `Principal.id` is stable across calls for the same
|
|
51
|
+
`name` (idempotent issue)."""
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
async def resolve(self, principal_id: str) -> Principal | None:
|
|
55
|
+
"""Return the principal with this id, or ``None`` if unknown."""
|
|
56
|
+
|
|
57
|
+
@abstractmethod
|
|
58
|
+
async def verify(self, token: str) -> Principal:
|
|
59
|
+
"""Verify an inbound credential and return its principal.
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
IdentityError: the token is missing, malformed, expired, or
|
|
63
|
+
not issued by this provider.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
@abstractmethod
|
|
67
|
+
async def credential(self, principal: Principal) -> str:
|
|
68
|
+
"""Mint an outbound credential (token / assertion) that another
|
|
69
|
+
party can `verify()` back into `principal`."""
|
|
70
|
+
|
|
71
|
+
@abstractmethod
|
|
72
|
+
async def rotate(self, principal_id: str) -> Principal:
|
|
73
|
+
"""Rotate the principal's signing material / credential. The
|
|
74
|
+
principal id is unchanged; previously minted credentials stop
|
|
75
|
+
verifying.
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
IdentityError: `principal_id` is unknown.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def capabilities(self) -> set[str]:
|
|
82
|
+
"""Optional capabilities this provider honours, from a closed
|
|
83
|
+
vocabulary (e.g. ``"rotation"``, ``"oidc"``, ``"spiffe"``).
|
|
84
|
+
Default: none."""
|
|
85
|
+
return set()
|
|
86
|
+
|
|
87
|
+
def supports(self, capability: str) -> bool:
|
|
88
|
+
return capability in self.capabilities()
|
|
@@ -17,6 +17,7 @@ from agentforge_core.testing.conformance import (
|
|
|
17
17
|
run_embedding_conformance,
|
|
18
18
|
run_graph_conformance,
|
|
19
19
|
run_hybrid_search_conformance,
|
|
20
|
+
run_identity_conformance,
|
|
20
21
|
run_input_validator_conformance,
|
|
21
22
|
run_memory_conformance,
|
|
22
23
|
run_output_validator_conformance,
|
|
@@ -33,6 +34,7 @@ __all__ = [
|
|
|
33
34
|
"run_embedding_conformance",
|
|
34
35
|
"run_graph_conformance",
|
|
35
36
|
"run_hybrid_search_conformance",
|
|
37
|
+
"run_identity_conformance",
|
|
36
38
|
"run_input_validator_conformance",
|
|
37
39
|
"run_memory_conformance",
|
|
38
40
|
"run_output_validator_conformance",
|
|
@@ -32,6 +32,7 @@ from agentforge_core.contracts.guardrails import (
|
|
|
32
32
|
OutputValidator,
|
|
33
33
|
ToolCallGate,
|
|
34
34
|
)
|
|
35
|
+
from agentforge_core.contracts.identity import IdentityError, IdentityProvider
|
|
35
36
|
from agentforge_core.contracts.memory import MemoryStore
|
|
36
37
|
from agentforge_core.contracts.reranker import Reranker
|
|
37
38
|
from agentforge_core.contracts.strategy import ReasoningStrategy
|
|
@@ -1162,3 +1163,74 @@ async def run_hybrid_search_conformance(store: VectorStore) -> None:
|
|
|
1162
1163
|
)
|
|
1163
1164
|
finally:
|
|
1164
1165
|
await store.delete(["a", "b", "c"])
|
|
1166
|
+
|
|
1167
|
+
|
|
1168
|
+
# ----------------------------------------------------------------------
|
|
1169
|
+
# Identity (feat-029)
|
|
1170
|
+
# ----------------------------------------------------------------------
|
|
1171
|
+
|
|
1172
|
+
|
|
1173
|
+
async def run_identity_conformance(provider: IdentityProvider) -> None:
|
|
1174
|
+
"""Assert an `IdentityProvider` honours the locked contract.
|
|
1175
|
+
|
|
1176
|
+
The provider must start empty of the principals issued here. Covers
|
|
1177
|
+
issue→resolve round-trip + idempotency, credential/verify round-trip,
|
|
1178
|
+
rotation, and the unknown/invalid paths.
|
|
1179
|
+
"""
|
|
1180
|
+
# 1. issue → resolve round-trip; issued fields are preserved.
|
|
1181
|
+
p = await provider.issue(
|
|
1182
|
+
name="invoice-reconciler",
|
|
1183
|
+
owner="finance-platform",
|
|
1184
|
+
attributes={"env": "prod"},
|
|
1185
|
+
)
|
|
1186
|
+
assert p.id, "issued principal must have a non-empty id"
|
|
1187
|
+
assert p.owner == "finance-platform", "issue must preserve owner"
|
|
1188
|
+
assert p.metadata.get("env") == "prod", "issue must preserve attributes"
|
|
1189
|
+
|
|
1190
|
+
resolved = await provider.resolve(p.id)
|
|
1191
|
+
assert resolved is not None, "resolve must find an issued principal"
|
|
1192
|
+
assert resolved.id == p.id, "resolve must return the same id"
|
|
1193
|
+
|
|
1194
|
+
# 2. issue is idempotent on name — same name → same id.
|
|
1195
|
+
again = await provider.issue(name="invoice-reconciler", owner="finance-platform")
|
|
1196
|
+
assert again.id == p.id, "issue must be idempotent on name (stable id)"
|
|
1197
|
+
|
|
1198
|
+
# 3. resolve of an unknown id returns None (no raise).
|
|
1199
|
+
assert await provider.resolve("agentforge:agent:nobody@0") is None, (
|
|
1200
|
+
"resolve of an unknown id must return None"
|
|
1201
|
+
)
|
|
1202
|
+
|
|
1203
|
+
# 4. credential → verify round-trip yields the same principal.
|
|
1204
|
+
token = await provider.credential(p)
|
|
1205
|
+
assert isinstance(token, str), "credential must return a string"
|
|
1206
|
+
assert token, "credential must return a non-empty token"
|
|
1207
|
+
verified = await provider.verify(token)
|
|
1208
|
+
assert verified.id == p.id, "verify(credential(p)) must round-trip to p"
|
|
1209
|
+
|
|
1210
|
+
# 5. verify of a bogus credential raises IdentityError.
|
|
1211
|
+
raised = False
|
|
1212
|
+
try:
|
|
1213
|
+
await provider.verify("not-a-real-credential")
|
|
1214
|
+
except IdentityError:
|
|
1215
|
+
raised = True
|
|
1216
|
+
assert raised, "verify of an invalid credential must raise IdentityError"
|
|
1217
|
+
|
|
1218
|
+
# 6. rotation keeps the id but invalidates prior credentials.
|
|
1219
|
+
rotated = await provider.rotate(p.id)
|
|
1220
|
+
assert rotated.id == p.id, "rotate must keep the principal id"
|
|
1221
|
+
stale = False
|
|
1222
|
+
try:
|
|
1223
|
+
await provider.verify(token)
|
|
1224
|
+
except IdentityError:
|
|
1225
|
+
stale = True
|
|
1226
|
+
assert stale, "a credential minted before rotate() must stop verifying"
|
|
1227
|
+
fresh = await provider.credential(rotated)
|
|
1228
|
+
assert (await provider.verify(fresh)).id == p.id, "post-rotation credential must verify"
|
|
1229
|
+
|
|
1230
|
+
# 7. rotate of an unknown principal raises IdentityError.
|
|
1231
|
+
unknown = False
|
|
1232
|
+
try:
|
|
1233
|
+
await provider.rotate("agentforge:agent:nobody@0")
|
|
1234
|
+
except IdentityError:
|
|
1235
|
+
unknown = True
|
|
1236
|
+
assert unknown, "rotate of an unknown principal must raise IdentityError"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Auth value types (feat-014; widened by feat-029).
|
|
2
|
+
|
|
3
|
+
`Principal` is the identity carried through the framework — returned by an
|
|
4
|
+
`AuthPolicy` after a successful authentication (feat-014) and issued /
|
|
5
|
+
resolved by an `IdentityProvider` (feat-029, governance identity). It
|
|
6
|
+
carries an opaque ``id`` (the token itself by default; a stable URN once a
|
|
7
|
+
real identity provider is wired — e.g.
|
|
8
|
+
``agentforge:agent:<org>/<name>@<version>``) plus optional ``kind`` /
|
|
9
|
+
``owner`` and a free-form ``metadata`` attribute bag (role tags, tenant
|
|
10
|
+
id, env, region, …).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class Principal:
|
|
20
|
+
"""Identity returned by `AuthPolicy.authenticate(...)` and issued /
|
|
21
|
+
resolved by `IdentityProvider` (feat-029).
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
id: Stable identifier — a bearer token for env-backed auth, or a
|
|
25
|
+
URN for a governance identity provider.
|
|
26
|
+
kind: What the principal *is* — ``"agent"`` (default), ``"tool"``,
|
|
27
|
+
``"service"``, or ``"human"``.
|
|
28
|
+
owner: The team / human accountable for the principal, if known.
|
|
29
|
+
metadata: Free-form string attributes (env, region, role tags,
|
|
30
|
+
tenant id) — the governance ``attributes`` bag.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
id: str
|
|
34
|
+
kind: str = "agent"
|
|
35
|
+
owner: str | None = None
|
|
36
|
+
metadata: dict[str, str] = field(default_factory=dict)
|
|
@@ -13,6 +13,8 @@ Pydantic model.
|
|
|
13
13
|
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
|
+
from typing import Literal
|
|
17
|
+
|
|
16
18
|
from pydantic import BaseModel, ConfigDict, Field
|
|
17
19
|
|
|
18
20
|
from agentforge_core.contracts.graph_store import GraphStore
|
|
@@ -32,6 +34,11 @@ class GraphExpansion(BaseModel):
|
|
|
32
34
|
fan-out — tune cautiously.
|
|
33
35
|
edge_types: If set, restricts traversal to these edge
|
|
34
36
|
types. ``None`` means all edge types.
|
|
37
|
+
direction: Which way to follow edges during expansion
|
|
38
|
+
(enh-005). ``"out"`` follows ``src → dst`` (e.g.
|
|
39
|
+
callees, what-X-cites); ``"in"`` follows ``dst → src``
|
|
40
|
+
(e.g. callers, who-cites-X); ``"any"`` (default) is the
|
|
41
|
+
original undirected behaviour via ``store.traverse``.
|
|
35
42
|
text_property: Graph-node property used to populate the
|
|
36
43
|
synthesised ``VectorMatch.text``. Defaults to
|
|
37
44
|
``"text"``.
|
|
@@ -49,5 +56,6 @@ class GraphExpansion(BaseModel):
|
|
|
49
56
|
store: GraphStore
|
|
50
57
|
max_hops: int = Field(default=2, ge=1)
|
|
51
58
|
edge_types: tuple[str, ...] | None = None
|
|
59
|
+
direction: Literal["out", "in", "any"] = "any"
|
|
52
60
|
text_property: str = Field(default="text", min_length=1)
|
|
53
61
|
decay: float = Field(default=0.5, gt=0.0, le=1.0)
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"""Auth value types (feat-014).
|
|
2
|
-
|
|
3
|
-
`Principal` is the identity returned by an `AuthPolicy` after a
|
|
4
|
-
successful authentication. Carries an opaque ``id`` (the token
|
|
5
|
-
itself by default; an opaque user id once a real identity
|
|
6
|
-
provider is wired) plus optional metadata for downstream use
|
|
7
|
-
(role tags, tenant id, etc.).
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
from __future__ import annotations
|
|
11
|
-
|
|
12
|
-
from dataclasses import dataclass, field
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@dataclass(frozen=True)
|
|
16
|
-
class Principal:
|
|
17
|
-
"""Identity returned by `AuthPolicy.authenticate(...)`."""
|
|
18
|
-
|
|
19
|
-
id: str
|
|
20
|
-
metadata: dict[str, str] = field(default_factory=dict)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/config/module_schemas.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/graph_store.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/protocol_bridge.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/contracts/vector_store.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/observability/__init__.py
RENAMED
|
File without changes
|
{agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/observability/tracing.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/production/exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/production/log_filter.py
RENAMED
|
File without changes
|
{agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/production/log_format.py
RENAMED
|
File without changes
|
{agentforge_core-0.3.0 → agentforge_core-0.4.0}/src/agentforge_core/production/run_context.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentforge_core-0.3.0 → agentforge_core-0.4.0}/tests/integration/test_config_imports_cli.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|