sopvm 0.2.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.
- sopvm-0.2.0/.api-baseline.json +22 -0
- sopvm-0.2.0/.coverage +0 -0
- sopvm-0.2.0/.github/workflows/adversarial.yml +31 -0
- sopvm-0.2.0/.github/workflows/release.yml +50 -0
- sopvm-0.2.0/.mimocode/.cron-lock +1 -0
- sopvm-0.2.0/.pre-commit-hooks.yaml +7 -0
- sopvm-0.2.0/ARCHITECTURE.md +81 -0
- sopvm-0.2.0/CHANGELOG.md +55 -0
- sopvm-0.2.0/CONTRIBUTING.md +83 -0
- sopvm-0.2.0/INTERFACES.md +231 -0
- sopvm-0.2.0/PKG-INFO +115 -0
- sopvm-0.2.0/README.md +96 -0
- sopvm-0.2.0/VERSIONING_POLICY.md +49 -0
- sopvm-0.2.0/examples/adversarial_walkthrough.md +121 -0
- sopvm-0.2.0/examples/compile-deny.ir.json +1 -0
- sopvm-0.2.0/examples/compiled-refund.ir.json +67 -0
- sopvm-0.2.0/examples/langgraph_integration.py +47 -0
- sopvm-0.2.0/examples/providers/mock_db.py +34 -0
- sopvm-0.2.0/examples/providers/mock_payments.py +25 -0
- sopvm-0.2.0/examples/sops/data-export.sop.yaml +42 -0
- sopvm-0.2.0/examples/sops/incident-response.sop.yaml +54 -0
- sopvm-0.2.0/examples/violating.ir.json +1 -0
- sopvm-0.2.0/policies/data-export.policy.yaml +9 -0
- sopvm-0.2.0/policies/incident-response.policy.yaml +14 -0
- sopvm-0.2.0/policies/support-agent.policy.yaml +7 -0
- sopvm-0.2.0/pyproject.toml +40 -0
- sopvm-0.2.0/schemas/capability.schema.json +25 -0
- sopvm-0.2.0/schemas/sop.schema.json +78 -0
- sopvm-0.2.0/scripts/check_api_compat.py +147 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/.github/workflows/ci.yml +30 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/README.md +124 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/cli/__init__.py +1 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/cli/main.py +92 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/docs/architecture.md +432 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/examples/hello-sop/hello.md +10 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/pyproject.toml +30 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/sopvm/__init__.py +10 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/sopvm/api.py +37 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/sopvm/compiler/__init__.py +11 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/sopvm/compiler/ast.py +80 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/sopvm/compiler/errors.py +56 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/sopvm/compiler/parser.py +283 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/tests/__init__.py +0 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/tests/unit/__init__.py +0 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/tests/unit/fixtures/missing_frontmatter.md +3 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/tests/unit/fixtures/valid.md +11 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/tests/unit/test_cli.py +117 -0
- sopvm-0.2.0/sopvm-milestone-1/sopvm/tests/unit/test_parser.py +338 -0
- sopvm-0.2.0/src/sopvm/__init__.py +92 -0
- sopvm-0.2.0/src/sopvm/capability/__init__.py +12 -0
- sopvm-0.2.0/src/sopvm/capability/policy.py +94 -0
- sopvm-0.2.0/src/sopvm/capability/token.py +159 -0
- sopvm-0.2.0/src/sopvm/checker/__init__.py +5 -0
- sopvm-0.2.0/src/sopvm/checker/check.py +176 -0
- sopvm-0.2.0/src/sopvm/cli/__init__.py +1 -0
- sopvm-0.2.0/src/sopvm/cli/main.py +177 -0
- sopvm-0.2.0/src/sopvm/compiler/__init__.py +8 -0
- sopvm-0.2.0/src/sopvm/compiler/errors.py +16 -0
- sopvm-0.2.0/src/sopvm/compiler/lower.py +61 -0
- sopvm-0.2.0/src/sopvm/compiler/page.py +53 -0
- sopvm-0.2.0/src/sopvm/compiler/pipeline.py +37 -0
- sopvm-0.2.0/src/sopvm/integrations/__init__.py +1 -0
- sopvm-0.2.0/src/sopvm/integrations/langgraph/__init__.py +1 -0
- sopvm-0.2.0/src/sopvm/integrations/langgraph/node.py +83 -0
- sopvm-0.2.0/src/sopvm/ir/__init__.py +5 -0
- sopvm-0.2.0/src/sopvm/ir/model.py +94 -0
- sopvm-0.2.0/src/sopvm/ir/versions.py +7 -0
- sopvm-0.2.0/src/sopvm/parser/__init__.py +15 -0
- sopvm-0.2.0/src/sopvm/parser/ast.py +62 -0
- sopvm-0.2.0/src/sopvm/parser/errors.py +53 -0
- sopvm-0.2.0/src/sopvm/parser/parse.py +151 -0
- sopvm-0.2.0/src/sopvm/plugins/__init__.py +13 -0
- sopvm-0.2.0/src/sopvm/plugins/base.py +53 -0
- sopvm-0.2.0/src/sopvm/plugins/registry.py +43 -0
- sopvm-0.2.0/src/sopvm/plugins/sandbox.py +63 -0
- sopvm-0.2.0/src/sopvm/runtime/__init__.py +18 -0
- sopvm-0.2.0/src/sopvm/runtime/events.py +30 -0
- sopvm-0.2.0/src/sopvm/runtime/executor.py +288 -0
- sopvm-0.2.0/src/sopvm/runtime/gate.py +75 -0
- sopvm-0.2.0/src/sopvm/runtime/state.py +27 -0
- sopvm-0.2.0/src/sopvm/runtime/violations.py +29 -0
- sopvm-0.2.0/src/sopvm/schemas/capability.schema.json +25 -0
- sopvm-0.2.0/src/sopvm/schemas/sop.schema.json +78 -0
- sopvm-0.2.0/src/sopvm/telemetry/__init__.py +12 -0
- sopvm-0.2.0/src/sopvm/telemetry/events.py +89 -0
- sopvm-0.2.0/src/sopvm/telemetry/sink.py +58 -0
- sopvm-0.2.0/src/sopvm.egg-info/PKG-INFO +14 -0
- sopvm-0.2.0/src/sopvm.egg-info/SOURCES.txt +12 -0
- sopvm-0.2.0/src/sopvm.egg-info/dependency_links.txt +1 -0
- sopvm-0.2.0/src/sopvm.egg-info/entry_points.txt +2 -0
- sopvm-0.2.0/src/sopvm.egg-info/requires.txt +7 -0
- sopvm-0.2.0/src/sopvm.egg-info/top_level.txt +1 -0
- sopvm-0.2.0/tests/__init__.py +0 -0
- sopvm-0.2.0/tests/adversarial/README.md +74 -0
- sopvm-0.2.0/tests/adversarial/__init__.py +0 -0
- sopvm-0.2.0/tests/adversarial/test_escalation.py +115 -0
- sopvm-0.2.0/tests/adversarial/test_gate.py +166 -0
- sopvm-0.2.0/tests/adversarial/test_grammar_exploits.py +83 -0
- sopvm-0.2.0/tests/adversarial/test_ir_tampering.py +128 -0
- sopvm-0.2.0/tests/adversarial/test_provider_integrity.py +107 -0
- sopvm-0.2.0/tests/fixtures/refund-request-handling.sop.yaml +35 -0
- sopvm-0.2.0/tests/golden/__init__.py +0 -0
- sopvm-0.2.0/tests/golden/refund-request-handling.ast.json +79 -0
- sopvm-0.2.0/tests/golden/refund-request-handling.ir.json +67 -0
- sopvm-0.2.0/tests/golden/test_golden.py +47 -0
- sopvm-0.2.0/tests/golden/test_ir_golden.py +49 -0
- sopvm-0.2.0/tests/integration/__init__.py +0 -0
- sopvm-0.2.0/tests/integration/test_checker.py +32 -0
- sopvm-0.2.0/tests/integration/test_langgraph.py +54 -0
- sopvm-0.2.0/tests/integration/test_parse.py +65 -0
- sopvm-0.2.0/tests/integration/test_telemetry.py +114 -0
- sopvm-0.2.0/tests/property/__init__.py +0 -0
- sopvm-0.2.0/tests/property/test_executor_property.py +91 -0
- sopvm-0.2.0/tests/property/test_gate_property.py +95 -0
- sopvm-0.2.0/tests/property/test_ir_property.py +114 -0
- sopvm-0.2.0/tests/property/test_page_property.py +84 -0
- sopvm-0.2.0/tests/property/test_property.py +103 -0
- sopvm-0.2.0/tests/test_import_boundaries.py +78 -0
- sopvm-0.2.0/tests/unit/__init__.py +0 -0
- sopvm-0.2.0/tests/unit/test_capability_grammar.py +67 -0
- sopvm-0.2.0/tests/unit/test_capability_token.py +78 -0
- sopvm-0.2.0/tests/unit/test_checker.py +142 -0
- sopvm-0.2.0/tests/unit/test_cli.py +137 -0
- sopvm-0.2.0/tests/unit/test_executor.py +184 -0
- sopvm-0.2.0/tests/unit/test_gate.py +76 -0
- sopvm-0.2.0/tests/unit/test_lower.py +124 -0
- sopvm-0.2.0/tests/unit/test_page.py +124 -0
- sopvm-0.2.0/tests/unit/test_pipeline.py +36 -0
- sopvm-0.2.0/tests/unit/test_plugins.py +99 -0
- sopvm-0.2.0/tests/unit/test_policy.py +67 -0
- sopvm-0.2.0/tests/unit/test_public_api.py +89 -0
- sopvm-0.2.0/tests/unit/test_schema.py +85 -0
- sopvm-0.2.0/tests/unit/test_telemetry.py +110 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compile": [
|
|
3
|
+
"sop_path",
|
|
4
|
+
"policy_path"
|
|
5
|
+
],
|
|
6
|
+
"check": [
|
|
7
|
+
"compiled"
|
|
8
|
+
],
|
|
9
|
+
"Runtime": [
|
|
10
|
+
"compiled",
|
|
11
|
+
"providers"
|
|
12
|
+
],
|
|
13
|
+
"__init__": [
|
|
14
|
+
"compiled",
|
|
15
|
+
"providers"
|
|
16
|
+
],
|
|
17
|
+
"run": [],
|
|
18
|
+
"execute": [
|
|
19
|
+
"node",
|
|
20
|
+
"request_tool"
|
|
21
|
+
]
|
|
22
|
+
}
|
sopvm-0.2.0/.coverage
ADDED
|
Binary file
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Adversarial Security Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [main]
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
adversarial:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: |
|
|
26
|
+
python -m pip install --upgrade pip
|
|
27
|
+
pip install -e ".[dev]"
|
|
28
|
+
|
|
29
|
+
- name: Run adversarial test suite
|
|
30
|
+
run: |
|
|
31
|
+
pytest tests/adversarial/ -v --tb=short
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Release to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Set up Python
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
|
|
22
|
+
- name: Install build dependencies
|
|
23
|
+
run: python -m pip install --upgrade pip build
|
|
24
|
+
|
|
25
|
+
- name: Build package
|
|
26
|
+
run: python -m build
|
|
27
|
+
|
|
28
|
+
- name: Upload build artifacts
|
|
29
|
+
uses: actions/upload-artifact@v4
|
|
30
|
+
with:
|
|
31
|
+
name: dist
|
|
32
|
+
path: dist/
|
|
33
|
+
|
|
34
|
+
publish:
|
|
35
|
+
needs: build
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
environment: release
|
|
38
|
+
permissions:
|
|
39
|
+
id-token: write # Required for PyPI trusted publishing (OIDC)
|
|
40
|
+
steps:
|
|
41
|
+
- name: Download build artifacts
|
|
42
|
+
uses: actions/download-artifact@v4
|
|
43
|
+
with:
|
|
44
|
+
name: dist
|
|
45
|
+
path: dist/
|
|
46
|
+
|
|
47
|
+
- name: Publish to PyPI
|
|
48
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
49
|
+
# Uses trusted publishing (OIDC) — no API token needed
|
|
50
|
+
# Configure at https://pypi.org/manage/project/sopvm/settings/publishing/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"pid":19240,"startedAt":1784354811742}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# ARCHITECTURE.md
|
|
2
|
+
|
|
3
|
+
SOPVM's architecture is built on one core principle: **the compiler never talks to a model, and the runtime never talks to raw SOP prose.** Everything the LLM sees at execution time came out of the compiler as a validated artifact.
|
|
4
|
+
|
|
5
|
+
## Parser (`sopvm.parser`)
|
|
6
|
+
|
|
7
|
+
**Milestone:** M2
|
|
8
|
+
|
|
9
|
+
Parses strict YAML SOP files into a typed AST. Validates against a JSON Schema (`schemas/sop.schema.json`) and performs semantic checks (edge target resolution, reachability, capability grammar).
|
|
10
|
+
|
|
11
|
+
**Design rationale:** YAML was chosen over Markdown (which M1 used) because it's unambiguous to parse and doesn't require a hand-written grammar. The JSON Schema provides machine-readable validation; the semantic checks catch logic errors that the schema can't express.
|
|
12
|
+
|
|
13
|
+
**What the parser does NOT do:** It doesn't know about policies, capabilities, or execution semantics. It only validates structure.
|
|
14
|
+
|
|
15
|
+
See [INTERFACES.md §1](INTERFACES.md#1-sop-source-format-owned-by-m2) for the exact YAML format.
|
|
16
|
+
|
|
17
|
+
## AST (`sopvm.parser.ast`)
|
|
18
|
+
|
|
19
|
+
**Milestone:** M2
|
|
20
|
+
|
|
21
|
+
Frozen dataclasses representing the parsed SOP: `SopDocument`, `StepNode`, `CapabilityRequest`. Immutable — the AST is a build artifact, not a mutable state object.
|
|
22
|
+
|
|
23
|
+
See [INTERFACES.md §2](INTERFACES.md#2-ast-owned-by-m2) for the exact shape.
|
|
24
|
+
|
|
25
|
+
## Compiler (`sopvm.compiler`)
|
|
26
|
+
|
|
27
|
+
**Milestones:** M3 (lowering), M5 (paging)
|
|
28
|
+
|
|
29
|
+
Two passes:
|
|
30
|
+
1. **Lower** (`lower.py`): Converts AST → IR (`CompiledProgram`). Direct 1:1 mapping, no optimization.
|
|
31
|
+
2. **Page** (`page.py`): Computes `capabilities_paged` by intersecting declared capabilities against the policy via `satisfies()`. This is where the policy ceiling is applied.
|
|
32
|
+
|
|
33
|
+
**Design rationale:** Separating lowering from paging makes each pass independently testable and keeps the IR clean. The IR is the *only* artifact the runtime reads — it never sees the AST or source YAML.
|
|
34
|
+
|
|
35
|
+
**Why compilation is deterministic:** No LLM calls, no randomness, no side effects. Same input → same output, always. This is what makes the compiler testable without an API key.
|
|
36
|
+
|
|
37
|
+
See [INTERFACES.md §4](INTERFACES.md#4-ir--program-graph-owned-by-m3-capability-annotated-by-m5) for the IR shape.
|
|
38
|
+
|
|
39
|
+
## Capability Model (`sopvm.capability`, `sopvm.checker`)
|
|
40
|
+
|
|
41
|
+
**Milestone:** M4
|
|
42
|
+
|
|
43
|
+
Two layers:
|
|
44
|
+
1. **Capability tokens** (`capability/token.py`): Parse `namespace:action(params)` strings into structured tokens. Comparator params (`<=`, `>=`) express ceilings in policy entries.
|
|
45
|
+
2. **Static checker** (`checker/check.py`): Validates that every declared capability in the IR satisfies at least one policy entry. This is compile-time enforcement.
|
|
46
|
+
|
|
47
|
+
**Design rationale:** Capabilities are unforgeable references, not strings the model can produce. The LLM never sees or manipulates a capability token directly — it only requests an action, and the gate decides whether the currently-held capability covers that action.
|
|
48
|
+
|
|
49
|
+
See [INTERFACES.md §3](INTERFACES.md#3-capability-token-schema-owned-by-m4) for the token schema and policy format.
|
|
50
|
+
|
|
51
|
+
## Runtime (`sopvm.runtime`)
|
|
52
|
+
|
|
53
|
+
**Milestones:** M6 (executor), M7 (gate), M8 (providers)
|
|
54
|
+
|
|
55
|
+
The runtime is a stack machine with an explicit program counter:
|
|
56
|
+
1. **Executor** (`executor.py`): Drives the state machine. Fixed transition table, not ad hoc branching.
|
|
57
|
+
2. **Capability gate** (`gate.py`): Intercepts every tool-call request. Checks against `capabilities_paged` for the current step. DENIED is a terminal fact — no retry, no recovery.
|
|
58
|
+
3. **Provider registry + sandbox** (`plugins/`): Routes approved calls to registered providers. The sandbox catches providers lying about their own scope (defense in depth).
|
|
59
|
+
|
|
60
|
+
**Design rationale:** The LLM is *never* trusted to control the program counter. It proposes ("I believe the precondition for branch B holds"), the engine decides. Every state transition is auditable via the telemetry system.
|
|
61
|
+
|
|
62
|
+
**IR version check:** The executor rejects IR files with unsupported versions before execution begins, preventing confusing mid-execution failures.
|
|
63
|
+
|
|
64
|
+
## Telemetry (`sopvm.telemetry`)
|
|
65
|
+
|
|
66
|
+
**Milestone:** M10
|
|
67
|
+
|
|
68
|
+
Every state transition and gate decision is emitted to a `TelemetrySink`. The `JsonlSink` produces append-only JSONL traces. Telemetry failures are silently degraded — a broken sink never crashes the run it's observing.
|
|
69
|
+
|
|
70
|
+
See [INTERFACES.md §7](INTERFACES.md#7-event--telemetry-schema-owned-by-m10) for the event schema.
|
|
71
|
+
|
|
72
|
+
## CLI (`sopvm.cli`)
|
|
73
|
+
|
|
74
|
+
**Milestone:** M11
|
|
75
|
+
|
|
76
|
+
Four subcommands: `compile`, `check`, `run`, `trace`. The `check` command is designed for pre-commit hooks (exit 1 on violations). No subcommand ever prints a raw Python traceback for expected failures.
|
|
77
|
+
|
|
78
|
+
## Integration Points
|
|
79
|
+
|
|
80
|
+
- **LangGraph** (`sopvm.integrations.langgraph`): Wraps a compiled SOP as a StateGraph node. This is the ONLY module allowed to import langgraph (enforced by AST-based import boundary test).
|
|
81
|
+
- **Pre-commit** (`.pre-commit-hooks.yaml`): `sopvm-check` hook for CI/CD pipelines.
|
sopvm-0.2.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to SOPVM will be documented in this file.
|
|
4
|
+
|
|
5
|
+
Format based on [Keep a Changelog](https://keepachangelog.com/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.0] - 2026-07-18
|
|
10
|
+
|
|
11
|
+
> **Versioning note:** Per pre-1.0 SemVer convention, MINOR bumps (0.x)
|
|
12
|
+
> may signal breaking changes. This release replaces the M1 public API
|
|
13
|
+
> (`compile_check()`, Markdown input, argparse CLI) with the M2-M13
|
|
14
|
+
> public API (`compile()`, `check()`, `Runtime`, YAML input, click CLI).
|
|
15
|
+
> See VERSIONING_POLICY.md for details.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- **M2**: SOP YAML parser with JSON Schema validation and semantic checks
|
|
19
|
+
- **M3**: AST→IR lowering pass (`CompiledProgram`, `IrNode`)
|
|
20
|
+
- **M4**: Capability token parser, policy loader, static checker (`satisfies()`, `check()`)
|
|
21
|
+
- **M5**: Paging pass (`apply_paging()`) and compile pipeline (`compile_sop()`)
|
|
22
|
+
- **M6**: Runtime executor with state machine, event hooks, infinite-loop detection
|
|
23
|
+
- **M7**: Capability gate — runtime tool-call interception and enforcement
|
|
24
|
+
- **M8**: Plugin architecture — `ToolProvider`, `ProviderRegistry`, sandbox wrapper
|
|
25
|
+
- **M9**: LangGraph integration adapter (`as_langgraph_node()`)
|
|
26
|
+
- **M10**: Telemetry — `Event`, `EventType` enum, `TelemetrySink`, `JsonlSink`
|
|
27
|
+
- **M11**: CLI with `compile`, `check`, `run`, `trace` subcommands (click)
|
|
28
|
+
- **M12**: Adversarial security test suite (25+ test cases, CI-gated)
|
|
29
|
+
- **M13**: Public API stabilization — `sopvm.compile()`, `sopvm.check()`, `sopvm.Runtime`
|
|
30
|
+
- Pre-commit hook (`sopvm-check`)
|
|
31
|
+
- IR version validation — `UnsupportedIrVersionError` for unsupported IR files
|
|
32
|
+
- API compatibility CI check script
|
|
33
|
+
- JSON Schema for SOP YAML format (`schemas/sop.schema.json`)
|
|
34
|
+
- JSON Schema for capability tokens (`schemas/capability.schema.json`)
|
|
35
|
+
|
|
36
|
+
### Security
|
|
37
|
+
- **M4**: Static capability checker catches policy ceiling violations at compile time
|
|
38
|
+
- **M7**: Runtime capability gate denies out-of-scope tool calls
|
|
39
|
+
- **M8**: Provider sandbox catches lying providers (defense in depth)
|
|
40
|
+
- **M12**: IR tampering detection — runtime re-validates `capabilities_paged` against policy
|
|
41
|
+
- **M12**: Adversarial test corpus covers 25+ escalation techniques
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
- **M5**: Replaced M3's placeholder `capabilities_paged = capabilities_declared` with real policy-resolved subset
|
|
45
|
+
- **M10**: Extended M6's stub `Event` with full telemetry schema (EventType enum, timestamps, run_id)
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
- **M12**: Fixed IR tampering gap — executor now re-validates capabilities_paged at run time when policy is provided
|
|
49
|
+
|
|
50
|
+
## [0.1.0] - 2026-07-18
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
- Initial release: Markdown+frontmatter SOP parser (M1)
|
|
54
|
+
- `sopvm compile --check` CLI command
|
|
55
|
+
- Unit tests for parser
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Contributing to SOPVM
|
|
2
|
+
|
|
3
|
+
## Development Setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Clone the repo
|
|
7
|
+
git clone https://github.com/your-org/sopvm.git
|
|
8
|
+
cd sopvm
|
|
9
|
+
|
|
10
|
+
# Install in editable mode with dev dependencies
|
|
11
|
+
pip install -e ".[dev]"
|
|
12
|
+
|
|
13
|
+
# Run the full test suite
|
|
14
|
+
make ci
|
|
15
|
+
# or equivalently:
|
|
16
|
+
pytest tests/ -v
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Project Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
src/sopvm/
|
|
23
|
+
├── parser/ # M2: YAML parser + AST
|
|
24
|
+
├── ir/ # M3: IR data model
|
|
25
|
+
├── compiler/ # M3+M5: lowering + paging
|
|
26
|
+
├── capability/ # M4: token parser + policy loader
|
|
27
|
+
├── checker/ # M4: static capability checker
|
|
28
|
+
├── runtime/ # M6-M8: executor, gate, violations
|
|
29
|
+
├── plugins/ # M8: tool providers + registry + sandbox
|
|
30
|
+
├── integrations/ # M9: LangGraph adapter (ONLY place langgraph is imported)
|
|
31
|
+
├── telemetry/ # M10: events + sinks
|
|
32
|
+
├── cli/ # M11: click CLI
|
|
33
|
+
tests/
|
|
34
|
+
├── unit/ # Compiler + capability + scheduler unit tests
|
|
35
|
+
├── integration/ # Runtime + pipeline integration tests
|
|
36
|
+
├── adversarial/ # M12: security test corpus (CI-gated)
|
|
37
|
+
├── golden/ # AST/IR JSON snapshot tests
|
|
38
|
+
├── property/ # Hypothesis property-based tests
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Running Tests
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Full suite
|
|
45
|
+
pytest tests/ -v
|
|
46
|
+
|
|
47
|
+
# Just adversarial tests (CI-gated)
|
|
48
|
+
pytest tests/adversarial/ -v
|
|
49
|
+
|
|
50
|
+
# With coverage
|
|
51
|
+
pytest tests/ --cov=src/sopvm --cov-report=term-missing
|
|
52
|
+
|
|
53
|
+
# Property-based tests only
|
|
54
|
+
pytest tests/property/ -v
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Test Taxonomy
|
|
58
|
+
|
|
59
|
+
| Category | Purpose | When to add |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `unit/` | Isolated component tests | Any new function/class |
|
|
62
|
+
| `integration/` | End-to-end pipeline tests | New milestone or cross-component feature |
|
|
63
|
+
| `adversarial/` | Security boundary tests | New escalation technique or enforcement layer |
|
|
64
|
+
| `golden/` | Snapshot regression tests | Any change to AST/IR output format |
|
|
65
|
+
| `property/` | Hypothesis fuzz tests | Any invariant that holds for all valid inputs |
|
|
66
|
+
|
|
67
|
+
## Proposing INTERFACES.md Changes
|
|
68
|
+
|
|
69
|
+
INTERFACES.md is the single source of truth for cross-milestone schemas. Changes require:
|
|
70
|
+
|
|
71
|
+
1. **Read the Change Control section** at the bottom of INTERFACES.md
|
|
72
|
+
2. **Version bump** to the affected schema
|
|
73
|
+
3. **Explicit call-out** in the milestone prompt listing which section changed and why
|
|
74
|
+
4. **Update all consuming modules** — a schema change in §2 (AST) must propagate to §3 (capability), §4 (IR), etc.
|
|
75
|
+
|
|
76
|
+
Do NOT silently redefine a schema. If you need a new field, add it (MINOR bump). If you need to remove or rename a field, that's a breaking change (MAJOR bump).
|
|
77
|
+
|
|
78
|
+
## Code Style
|
|
79
|
+
|
|
80
|
+
- Python 3.10+ only
|
|
81
|
+
- No comments unless the WHY is non-obvious
|
|
82
|
+
- Frozen dataclasses for all data structures
|
|
83
|
+
- No runtime state mutations in the compiler/checker path
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# SOPVM — INTERFACES.md (canonical contract doc)
|
|
2
|
+
|
|
3
|
+
Status: DRAFT skeleton. Every later milestone prompt must conform to this or explicitly call out a proposed change here first. This file is the single source of truth for cross-milestone shapes — no milestone may silently redefine a schema it doesn't own.
|
|
4
|
+
|
|
5
|
+
Ownership: each section names the milestone that *defines* it. Other milestones may *consume* it but not *redefine* it.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. SOP Source Format (owned by M2)
|
|
10
|
+
|
|
11
|
+
Strict YAML, validated against a JSON Schema (`schemas/sop.schema.json`). No free-form prose fields feed the compiler — only structured fields do.
|
|
12
|
+
|
|
13
|
+
```yaml
|
|
14
|
+
sop_version: "0.1"
|
|
15
|
+
name: "refund-request-handling"
|
|
16
|
+
policy: "policies/support-agent.policy.yaml" # top-level capability ceiling (see §3)
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- id: verify_identity
|
|
20
|
+
description: "Confirm requester identity via order lookup"
|
|
21
|
+
requires:
|
|
22
|
+
capabilities: ["db:read(orders)"]
|
|
23
|
+
on_success: check_eligibility
|
|
24
|
+
on_failure: escalate_human
|
|
25
|
+
|
|
26
|
+
- id: check_eligibility
|
|
27
|
+
description: "Check refund policy eligibility"
|
|
28
|
+
requires:
|
|
29
|
+
capabilities: ["db:read(orders)", "db:read(policy_rules)"]
|
|
30
|
+
on_success: issue_refund
|
|
31
|
+
on_failure: escalate_human
|
|
32
|
+
|
|
33
|
+
- id: issue_refund
|
|
34
|
+
description: "Issue refund via payment provider"
|
|
35
|
+
requires:
|
|
36
|
+
capabilities: ["payments:refund(max_amount=100.00)"]
|
|
37
|
+
on_success: notify_user
|
|
38
|
+
on_failure: escalate_human
|
|
39
|
+
|
|
40
|
+
- id: notify_user
|
|
41
|
+
terminal: true
|
|
42
|
+
requires:
|
|
43
|
+
capabilities: ["notify:email"]
|
|
44
|
+
|
|
45
|
+
- id: escalate_human
|
|
46
|
+
terminal: true
|
|
47
|
+
requires:
|
|
48
|
+
capabilities: ["notify:slack(channel=support-escalations)"]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Rules M2 must enforce at parse time:
|
|
52
|
+
- every `on_success`/`on_failure` target must resolve to a declared step id
|
|
53
|
+
- every non-terminal step must declare at least one outgoing edge
|
|
54
|
+
- `capabilities` entries must match the capability grammar in §3
|
|
55
|
+
- unreferenced steps (unreachable from the declared entry point) are a parse error, not a warning
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 2. AST (owned by M2)
|
|
60
|
+
|
|
61
|
+
Internal only — not a public contract, but shape is fixed here so M3 doesn't guess:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
SopDocument
|
|
65
|
+
├── version: str
|
|
66
|
+
├── policy_ref: str
|
|
67
|
+
└── steps: list[StepNode]
|
|
68
|
+
StepNode
|
|
69
|
+
├── id: str
|
|
70
|
+
├── description: str
|
|
71
|
+
├── requires: CapabilityRequest[]
|
|
72
|
+
├── edges: {on_success: str|None, on_failure: str|None}
|
|
73
|
+
└── terminal: bool
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 3. Capability Token Schema (owned by M4)
|
|
79
|
+
|
|
80
|
+
Auditable outside Python — `schemas/capability.schema.json`.
|
|
81
|
+
|
|
82
|
+
Grammar: `namespace:action(param=value, ...)` — parameterized, not a flat string enum (see MILESTONES.md risk #2).
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
87
|
+
"title": "CapabilityToken",
|
|
88
|
+
"type": "object",
|
|
89
|
+
"required": ["namespace", "action", "params"],
|
|
90
|
+
"properties": {
|
|
91
|
+
"namespace": { "type": "string", "examples": ["fs", "net", "db", "payments", "notify"] },
|
|
92
|
+
"action": { "type": "string", "examples": ["read", "write", "http", "refund"] },
|
|
93
|
+
"params": { "type": "object", "additionalProperties": true },
|
|
94
|
+
"raw": { "type": "string", "description": "original string form, e.g. payments:refund(max_amount=100.00)" }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Policy file (top-level ceiling a SOP may never exceed):
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
# policies/support-agent.policy.yaml
|
|
103
|
+
policy_version: "0.1"
|
|
104
|
+
allowed_capabilities:
|
|
105
|
+
- "db:read(orders)"
|
|
106
|
+
- "db:read(policy_rules)"
|
|
107
|
+
- "payments:refund(max_amount<=100.00)"
|
|
108
|
+
- "notify:email"
|
|
109
|
+
- "notify:slack(channel=support-escalations)"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Static checker (M4) contract: given a compiled IR + a policy file, return a `CheckResult`:
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"passed": false,
|
|
117
|
+
"violations": [
|
|
118
|
+
{
|
|
119
|
+
"step_id": "issue_refund",
|
|
120
|
+
"requested": "payments:refund(max_amount=250.00)",
|
|
121
|
+
"reason": "exceeds policy ceiling payments:refund(max_amount<=100.00)"
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
This must be checkable **without running the runtime** — pure static analysis, usable as a pre-commit hook (M11).
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 4. IR / Program Graph (owned by M3, capability-annotated by M5)
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"ir_version": "0.1",
|
|
136
|
+
"entry": "verify_identity",
|
|
137
|
+
"nodes": {
|
|
138
|
+
"verify_identity": {
|
|
139
|
+
"capabilities_declared": ["db:read(orders)"],
|
|
140
|
+
"capabilities_paged": ["db:read(orders)"],
|
|
141
|
+
"edges": { "on_success": "check_eligibility", "on_failure": "escalate_human" },
|
|
142
|
+
"terminal": false
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
- `capabilities_declared`: what the source SOP asked for at this step (from AST, unchanged by compiler).
|
|
149
|
+
- `capabilities_paged`: what M5's paging-plan pass actually resolves as grantable *at this step* (may be a subset after policy intersection — never a superset of `capabilities_declared`).
|
|
150
|
+
- IR is the only artifact the runtime (M6) ever reads. The runtime never sees the AST or source YAML directly.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 5. Runtime State Machine (owned by M6)
|
|
155
|
+
|
|
156
|
+
States per step: `PENDING → RUNNING → {DONE, FAILED, DENIED}`.
|
|
157
|
+
|
|
158
|
+
- `DENIED` is a distinct terminal state from `FAILED` — a capability violation is not an execution error, it's a policy event, and M10's telemetry must be able to tell them apart.
|
|
159
|
+
- Transition rules are a fixed table, not branching logic scattered through the executor — this table is what M12's adversarial tests assert against.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 6. Tool Provider / Plugin Interface (owned by M8)
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
class ToolProvider(Protocol):
|
|
167
|
+
def declared_capabilities(self) -> list[str]: ...
|
|
168
|
+
def invoke(self, capability: CapabilityToken, args: dict) -> ToolResult: ...
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
- `invoke` is never called directly by step logic — only by the gate (M7), which checks `capability` against `capabilities_paged` for the current step first.
|
|
172
|
+
- A provider that lies about `declared_capabilities()` (requests more at invoke time than it declared) is itself a `DENIED` event, not a crash.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## 7. Event / Telemetry Schema (owned by M10)
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"event": "capability_denied",
|
|
181
|
+
"step_id": "issue_refund",
|
|
182
|
+
"requested": "payments:refund(max_amount=250.00)",
|
|
183
|
+
"paged": ["payments:refund(max_amount<=100.00)"],
|
|
184
|
+
"timestamp": "2026-07-18T12:00:00Z",
|
|
185
|
+
"run_id": "..."
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Event types (fixed enum, extend via versioning policy only): `step_started`, `step_completed`, `step_failed`, `capability_granted`, `capability_denied`, `run_started`, `run_completed`.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## 8. Public API (owned by M13, staged incrementally by M3/M6/M11)
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
sopvm.compile(sop_path: str, policy_path: str) -> CompiledProgram
|
|
197
|
+
sopvm.check(compiled: CompiledProgram) -> CheckResult
|
|
198
|
+
sopvm.Runtime(compiled: CompiledProgram, providers: list[ToolProvider])
|
|
199
|
+
.run() -> RunResult
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Anything not in this list is internal and may change without a version bump.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 9. CLI Contract (owned by M11)
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
sopvm compile <sop.yaml> --policy <policy.yaml> -o <out.ir.json>
|
|
210
|
+
sopvm check <out.ir.json> --policy <policy.yaml> # exit 1 on violation, for pre-commit
|
|
211
|
+
sopvm run <out.ir.json> --providers <providers.yaml>
|
|
212
|
+
sopvm trace <run_id> # replay telemetry
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## 10. LangGraph Adapter (owned by M9)
|
|
218
|
+
|
|
219
|
+
Adapter module only — must not leak LangGraph types into M1–M8. Exposes SOPVM as a single callable node:
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
def as_langgraph_node(compiled: CompiledProgram, providers: list[ToolProvider]) -> Callable
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Change control
|
|
228
|
+
|
|
229
|
+
Any edit to a section above after its owning milestone has merged requires:
|
|
230
|
+
1. A version bump to the affected schema (see VERSIONING_POLICY.md)
|
|
231
|
+
2. An explicit call-out in the milestone prompt that changes it, listing exactly which section of this file is being modified and why
|
sopvm-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sopvm
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A compiler and capability-gated runtime for Standard Operating Procedures executed by LLM agents.
|
|
5
|
+
Author: Sushit
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: click>=8.0
|
|
9
|
+
Requires-Dist: jsonschema>=4.20
|
|
10
|
+
Requires-Dist: pyyaml>=6.0
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: build; extra == 'dev'
|
|
13
|
+
Requires-Dist: hypothesis>=6.90; extra == 'dev'
|
|
14
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
16
|
+
Provides-Extra: langgraph
|
|
17
|
+
Requires-Dist: langgraph>=0.2.0; extra == 'langgraph'
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# SOPVM
|
|
21
|
+
|
|
22
|
+
**A runtime layer beneath agent frameworks — not another one.**
|
|
23
|
+
|
|
24
|
+
SOPVM compiles Standard Operating Procedures into executable programs and runs them on a capability-gated stack machine. It sits *underneath* LangGraph, CrewAI, or any tool-calling loop — the same way LLVM sits underneath Clang, Rust, and Swift rather than competing with any single language.
|
|
25
|
+
|
|
26
|
+
## Why Compilation Matters
|
|
27
|
+
|
|
28
|
+
Most "follow the SOP" approaches today mean "paste the SOP text into a system prompt and hope." The LLM re-plans every step, inconsistently. SOPVM compiles the SOP once into a deterministic, auditable program. The LLM only does the semantic work (interpreting what a step means); the runtime handles state tracking, capability enforcement, and execution flow. This is measurably better — the research paper behind SOPVM shows up to 16-point accuracy gains over raw prose.
|
|
29
|
+
|
|
30
|
+
## Quickstart
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Install
|
|
34
|
+
pip install -e ".[dev]"
|
|
35
|
+
|
|
36
|
+
# Compile a SOP against a policy
|
|
37
|
+
sopvm compile examples/sops/refund-request-handling.sop.yaml \
|
|
38
|
+
--policy policies/support-agent.policy.yaml \
|
|
39
|
+
-o compiled.ir.json
|
|
40
|
+
|
|
41
|
+
# Check for policy violations (pre-commit mode)
|
|
42
|
+
sopvm check compiled.ir.json --policy policies/support-agent.policy.yaml
|
|
43
|
+
# exit 0 = all capabilities within policy
|
|
44
|
+
# exit 1 = violations found
|
|
45
|
+
|
|
46
|
+
# Execute the compiled SOP
|
|
47
|
+
sopvm run compiled.ir.json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Example: compile and check
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
$ sopvm compile tests/fixtures/refund-request-handling.sop.yaml \
|
|
54
|
+
--policy policies/support-agent.policy.yaml \
|
|
55
|
+
-o compiled.ir.json
|
|
56
|
+
Compiled to compiled.ir.json
|
|
57
|
+
|
|
58
|
+
$ sopvm check compiled.ir.json --policy policies/support-agent.policy.yaml
|
|
59
|
+
All capabilities within policy.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Example: policy violation caught at check time
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
$ sopvm check violating.ir.json --policy policies/support-agent.policy.yaml
|
|
66
|
+
VIOLATION step=a requested='payments:refund(max_amount=250.00)' reason=exceeds policy ceiling payments:refund(max_amount<=100.00)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Example: runtime execution
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
$ sopvm run compiled.ir.json
|
|
73
|
+
Final state: DONE
|
|
74
|
+
Path: verify_identity -> check_eligibility -> issue_refund -> notify_user
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Architecture
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
SOP YAML ──► Parser ──► AST ──► Compiler ──► IR ──► Runtime ──► Result
|
|
81
|
+
(M2) (M2) (M3+M5) (M3) (M6-M8)
|
|
82
|
+
┌──────┴──────┐
|
|
83
|
+
│ Gate │Plugins│
|
|
84
|
+
│ (M7) │ (M8) │
|
|
85
|
+
└──────┴──────┘
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
| Box | Module | Milestone |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| Parser | `sopvm.parser` | M2 |
|
|
91
|
+
| AST | `sopvm.parser.ast` | M2 |
|
|
92
|
+
| Compiler (lower+page) | `sopvm.compiler` | M3, M5 |
|
|
93
|
+
| IR | `sopvm.ir` | M3 |
|
|
94
|
+
| Runtime (gate+providers) | `sopvm.runtime` | M6-M8 |
|
|
95
|
+
| Static Checker | `sopvm.checker` | M4 |
|
|
96
|
+
| Telemetry | `sopvm.telemetry` | M10 |
|
|
97
|
+
| CLI | `sopvm.cli` | M11 |
|
|
98
|
+
| LangGraph Adapter | `sopvm.integrations.langgraph` | M9 |
|
|
99
|
+
|
|
100
|
+
See [ARCHITECTURE.md](ARCHITECTURE.md) for detailed design rationale.
|
|
101
|
+
|
|
102
|
+
## Documentation
|
|
103
|
+
|
|
104
|
+
- [ARCHITECTURE.md](ARCHITECTURE.md) — component design and rationale
|
|
105
|
+
- [INTERFACES.md](INTERFACES.md) — canonical contract schemas (M2-M13)
|
|
106
|
+
- [VERSIONING_POLICY.md](VERSIONING_POLICY.md) — SemVer rules
|
|
107
|
+
- [CHANGELOG.md](CHANGELOG.md) — release history
|
|
108
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md) — development setup
|
|
109
|
+
|
|
110
|
+
## Examples
|
|
111
|
+
|
|
112
|
+
- `examples/sops/` — example SOP YAML files
|
|
113
|
+
- `examples/providers/` — reference tool provider implementations
|
|
114
|
+
- `examples/langgraph_integration.py` — LangGraph StateGraph wrapper
|
|
115
|
+
- `examples/adversarial_walkthrough.md` — step-by-step escalation attempt
|