lisa-harness-sdk 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lisa_harness_sdk-0.1.0/.gitignore +33 -0
- lisa_harness_sdk-0.1.0/.instructions.md +12 -0
- lisa_harness_sdk-0.1.0/AUTHORING.md +371 -0
- lisa_harness_sdk-0.1.0/PKG-INFO +21 -0
- lisa_harness_sdk-0.1.0/README.md +79 -0
- lisa_harness_sdk-0.1.0/packs/.instructions.md +12 -0
- lisa_harness_sdk-0.1.0/packs/README.md +47 -0
- lisa_harness_sdk-0.1.0/packs/core/pyproject.toml +35 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/__init__.py +14 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/prompts/sub_turn/budget_exhausted.md +3 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/prompts/sub_turn/delegate_contract.md +8 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/prompts/sub_turn/fork_contract.md +8 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/prompts/sub_turn/midpoint_write_nudge.md +6 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/prompts/sub_turn/no_write_warning.md +4 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/prompts/system-prompt.md +9 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/prompts/thread_manifest_hint.md +37 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/redaction_patterns.yml +46 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/modes/default/_mode.md +17 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/modes/explainer/_mode.md +43 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/modes/planner/_mode.md +41 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/__init__.py +1 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/delegation/__init__.py +1 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/delegation/delegate.md +61 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/delegation/delegate.py +198 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/delegation/delegate.yml +14 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/delegation/fork.md +73 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/delegation/fork.py +232 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/delegation/fork.yml +15 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/planning/__init__.py +1 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/planning/plan_workflow.py +691 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/planning/plan_workflow.yml +14 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/sessions/default.md +116 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/tools/__init__.py +0 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/tools/fetch_page/__init__.py +0 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/tools/fetch_page/_models.py +21 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/tools/fetch_page/fetch_page.py +251 -0
- lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/tools/fetch_page/fetch_page.tool.yml +9 -0
- lisa_harness_sdk-0.1.0/packs/core/tests/test_delegate_fn.py +186 -0
- lisa_harness_sdk-0.1.0/packs/core/tests/test_delegate_operation.py +195 -0
- lisa_harness_sdk-0.1.0/packs/core/tests/test_fetch_page_tool.py +112 -0
- lisa_harness_sdk-0.1.0/packs/core/tests/test_fork_fn.py +204 -0
- lisa_harness_sdk-0.1.0/packs/core/tests/test_fork_operation.py +381 -0
- lisa_harness_sdk-0.1.0/packs/core/tests/test_plan_workflow_settings.py +26 -0
- lisa_harness_sdk-0.1.0/packs/introspect/pyproject.toml +30 -0
- lisa_harness_sdk-0.1.0/packs/introspect/src/sdk/packs/introspect/__init__.py +29 -0
- lisa_harness_sdk-0.1.0/packs/introspect/src/sdk/packs/introspect/operations/__init__.py +1 -0
- lisa_harness_sdk-0.1.0/packs/introspect/src/sdk/packs/introspect/operations/self_model/__init__.py +1 -0
- lisa_harness_sdk-0.1.0/packs/introspect/src/sdk/packs/introspect/operations/self_model/reflect_self.operation.md +45 -0
- lisa_harness_sdk-0.1.0/packs/introspect/src/sdk/packs/introspect/operations/self_model/reflect_self.py +32 -0
- lisa_harness_sdk-0.1.0/packs/introspect/src/sdk/packs/introspect/operations/self_model/self_model.py +458 -0
- lisa_harness_sdk-0.1.0/packs/introspect/src/sdk/packs/introspect/operations/self_model/self_model.yml +5 -0
- lisa_harness_sdk-0.1.0/packs/memory/pyproject.toml +30 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/__init__.py +14 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/mcp_servers.yaml +52 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/__init__.py +1 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/consolidate_semantic.py +375 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/consolidate_semantic.yml +9 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/dispute.py +263 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/dispute.yml +10 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/mine_thread_segment.py +381 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/mine_thread_segment.yml +9 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/prompts/consolidate_semantic_system.md +10 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/prompts/mine_thread_segment_system.md +26 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/prompts/synthesize_learning_system.md +18 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/recall.py +210 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/recall.yml +10 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/synthesize_learning.py +430 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/synthesize_learning.yml +13 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/slice_provider.py +715 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/utils/__init__.py +1 -0
- lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/utils/memory_recall.py +140 -0
- lisa_harness_sdk-0.1.0/packs/memory/tests/test_consolidate_semantic.py +223 -0
- lisa_harness_sdk-0.1.0/packs/memory/tests/test_consolidate_semantic_branches.py +495 -0
- lisa_harness_sdk-0.1.0/packs/memory/tests/test_dispute_memory.py +433 -0
- lisa_harness_sdk-0.1.0/packs/memory/tests/test_distill_parse.py +73 -0
- lisa_harness_sdk-0.1.0/packs/memory/tests/test_distill_retry.py +180 -0
- lisa_harness_sdk-0.1.0/packs/memory/tests/test_memory_recall.py +370 -0
- lisa_harness_sdk-0.1.0/packs/memory/tests/test_mine_thread_segment.py +120 -0
- lisa_harness_sdk-0.1.0/packs/memory/tests/test_recall_branches.py +310 -0
- lisa_harness_sdk-0.1.0/packs/memory/tests/test_slice_provider.py +403 -0
- lisa_harness_sdk-0.1.0/packs/memory/tests/test_synthesize_learning_branches.py +437 -0
- lisa_harness_sdk-0.1.0/packs/memory/tests/test_synthesize_learning_tier.py +101 -0
- lisa_harness_sdk-0.1.0/packs/recovery/pyproject.toml +34 -0
- lisa_harness_sdk-0.1.0/packs/recovery/src/sdk/packs/recovery/README.md +58 -0
- lisa_harness_sdk-0.1.0/packs/recovery/src/sdk/packs/recovery/__init__.py +29 -0
- lisa_harness_sdk-0.1.0/packs/recovery/src/sdk/packs/recovery/commands.py +548 -0
- lisa_harness_sdk-0.1.0/packs/recovery/tests/e2e/conftest.py +1 -0
- lisa_harness_sdk-0.1.0/packs/recovery/tests/e2e/session_context.md +27 -0
- lisa_harness_sdk-0.1.0/packs/recovery/tests/e2e/skip_all.scenario.yml +29 -0
- lisa_harness_sdk-0.1.0/packs/recovery/tests/e2e/stall_inspect_skip.scenario.yml +29 -0
- lisa_harness_sdk-0.1.0/packs/recovery/tests/test_recovery_commands.py +443 -0
- lisa_harness_sdk-0.1.0/packs/recovery/tests/test_recovery_guards.py +512 -0
- lisa_harness_sdk-0.1.0/packs/recovery/tests/test_resume.py +156 -0
- lisa_harness_sdk-0.1.0/packs/recovery/tests/test_resume_extra.py +212 -0
- lisa_harness_sdk-0.1.0/pyproject.toml +75 -0
- lisa_harness_sdk-0.1.0/src/sdk/_content_root.py +20 -0
- lisa_harness_sdk-0.1.0/src/sdk/config.py +81 -0
- lisa_harness_sdk-0.1.0/src/sdk/content/__init__.py +117 -0
- lisa_harness_sdk-0.1.0/src/sdk/content/_catalog.py +1591 -0
- lisa_harness_sdk-0.1.0/src/sdk/content/_copilot_customizations.py +515 -0
- lisa_harness_sdk-0.1.0/src/sdk/content/_copilot_discovery.py +492 -0
- lisa_harness_sdk-0.1.0/src/sdk/content/_diagnostics.py +228 -0
- lisa_harness_sdk-0.1.0/src/sdk/content/_fetcher.py +517 -0
- lisa_harness_sdk-0.1.0/src/sdk/content/_front_matter.py +75 -0
- lisa_harness_sdk-0.1.0/src/sdk/content/_loader_strict.py +111 -0
- lisa_harness_sdk-0.1.0/src/sdk/content/_reader.py +88 -0
- lisa_harness_sdk-0.1.0/src/sdk/content/_roots.py +70 -0
- lisa_harness_sdk-0.1.0/src/sdk/content/py.typed +0 -0
- lisa_harness_sdk-0.1.0/src/sdk/context/__init__.py +1 -0
- lisa_harness_sdk-0.1.0/src/sdk/context/extension_item_builder.py +133 -0
- lisa_harness_sdk-0.1.0/src/sdk/context/tests/test_extension_item_builder.py +157 -0
- lisa_harness_sdk-0.1.0/src/sdk/guard/__init__.py +35 -0
- lisa_harness_sdk-0.1.0/src/sdk/guard/_backend.py +136 -0
- lisa_harness_sdk-0.1.0/src/sdk/guard/tests/__init__.py +0 -0
- lisa_harness_sdk-0.1.0/src/sdk/guard/tests/test_backend.py +114 -0
- lisa_harness_sdk-0.1.0/src/sdk/identity.py +48 -0
- lisa_harness_sdk-0.1.0/src/sdk/layout.py +35 -0
- lisa_harness_sdk-0.1.0/src/sdk/logging_setup.py +433 -0
- lisa_harness_sdk-0.1.0/src/sdk/mcp_host/__init__.py +43 -0
- lisa_harness_sdk-0.1.0/src/sdk/mcp_host/content_servers.py +223 -0
- lisa_harness_sdk-0.1.0/src/sdk/memory/__init__.py +5 -0
- lisa_harness_sdk-0.1.0/src/sdk/memory/backup.py +185 -0
- lisa_harness_sdk-0.1.0/src/sdk/memory/embeddings.py +50 -0
- lisa_harness_sdk-0.1.0/src/sdk/memory/store.py +785 -0
- lisa_harness_sdk-0.1.0/src/sdk/memory/tests/test_backup.py +143 -0
- lisa_harness_sdk-0.1.0/src/sdk/memory/tests/test_store.py +767 -0
- lisa_harness_sdk-0.1.0/src/sdk/pack_settings.py +61 -0
- lisa_harness_sdk-0.1.0/src/sdk/packs/__init__.py +13 -0
- lisa_harness_sdk-0.1.0/src/sdk/providers/__init__.py +77 -0
- lisa_harness_sdk-0.1.0/src/sdk/providers/anthropic_gateway.py +636 -0
- lisa_harness_sdk-0.1.0/src/sdk/providers/bedrock_gateway.py +376 -0
- lisa_harness_sdk-0.1.0/src/sdk/providers/google_gateway.py +574 -0
- lisa_harness_sdk-0.1.0/src/sdk/providers/openai_gateway.py +1765 -0
- lisa_harness_sdk-0.1.0/src/sdk/providers/token_counting.py +31 -0
- lisa_harness_sdk-0.1.0/src/sdk/py.typed +0 -0
- lisa_harness_sdk-0.1.0/src/sdk/router/README.md +682 -0
- lisa_harness_sdk-0.1.0/src/sdk/router/__init__.py +68 -0
- lisa_harness_sdk-0.1.0/src/sdk/router/default_config.py +34 -0
- lisa_harness_sdk-0.1.0/src/sdk/router/router.py +248 -0
- lisa_harness_sdk-0.1.0/src/sdk/storage/__init__.py +21 -0
- lisa_harness_sdk-0.1.0/src/sdk/storage/_backend.py +439 -0
- lisa_harness_sdk-0.1.0/src/sdk/storage/tests/__init__.py +0 -0
- lisa_harness_sdk-0.1.0/src/sdk/storage/tests/test_backend.py +343 -0
- lisa_harness_sdk-0.1.0/src/sdk/thread/__init__.py +48 -0
- lisa_harness_sdk-0.1.0/src/sdk/thread/_backend.py +755 -0
- lisa_harness_sdk-0.1.0/src/sdk/thread/_readout.py +587 -0
- lisa_harness_sdk-0.1.0/src/sdk/thread/_results.py +501 -0
- lisa_harness_sdk-0.1.0/src/sdk/thread/_search.py +171 -0
- lisa_harness_sdk-0.1.0/src/sdk/thread/_taskview.py +255 -0
- lisa_harness_sdk-0.1.0/src/sdk/thread/redaction.py +117 -0
- lisa_harness_sdk-0.1.0/src/sdk/workproducts.py +178 -0
- lisa_harness_sdk-0.1.0/src/sdk/workspace.py +112 -0
- lisa_harness_sdk-0.1.0/tests/conftest.py +46 -0
- lisa_harness_sdk-0.1.0/tests/test_anthropic_gateway.py +721 -0
- lisa_harness_sdk-0.1.0/tests/test_azure_gateway.py +505 -0
- lisa_harness_sdk-0.1.0/tests/test_bedrock_gateway.py +368 -0
- lisa_harness_sdk-0.1.0/tests/test_catalog_approval_continuation.py +51 -0
- lisa_harness_sdk-0.1.0/tests/test_catalog_module_path.py +140 -0
- lisa_harness_sdk-0.1.0/tests/test_catalog_timeout_zero.py +199 -0
- lisa_harness_sdk-0.1.0/tests/test_content_catalog_mode_provider_preference.py +192 -0
- lisa_harness_sdk-0.1.0/tests/test_copilot_customizations.py +539 -0
- lisa_harness_sdk-0.1.0/tests/test_default_available_capabilities.py +659 -0
- lisa_harness_sdk-0.1.0/tests/test_fetcher.py +415 -0
- lisa_harness_sdk-0.1.0/tests/test_gateway_streaming.py +402 -0
- lisa_harness_sdk-0.1.0/tests/test_google_gateway.py +480 -0
- lisa_harness_sdk-0.1.0/tests/test_import_boundaries.py +83 -0
- lisa_harness_sdk-0.1.0/tests/test_loader_strict.py +94 -0
- lisa_harness_sdk-0.1.0/tests/test_mcp_host_content_servers.py +252 -0
- lisa_harness_sdk-0.1.0/tests/test_memory_embeddings.py +104 -0
- lisa_harness_sdk-0.1.0/tests/test_merge_child_usage.py +150 -0
- lisa_harness_sdk-0.1.0/tests/test_openai_compatible_gateway.py +503 -0
- lisa_harness_sdk-0.1.0/tests/test_overlay_catalog.py +336 -0
- lisa_harness_sdk-0.1.0/tests/test_plan_mode_escalation.py +999 -0
- lisa_harness_sdk-0.1.0/tests/test_reflect_self.py +98 -0
- lisa_harness_sdk-0.1.0/tests/test_request_token_counting.py +53 -0
- lisa_harness_sdk-0.1.0/tests/test_router.py +710 -0
- lisa_harness_sdk-0.1.0/tests/test_self_model_telemetry.py +178 -0
- lisa_harness_sdk-0.1.0/tests/test_thread_archive_reads.py +485 -0
- lisa_harness_sdk-0.1.0/tests/test_thread_search.py +1843 -0
- lisa_harness_sdk-0.1.0/tests/test_tool_stream_output.py +81 -0
- lisa_harness_sdk-0.1.0/tests/test_turn_runner_per_model_usage.py +321 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.pyo
|
|
5
|
+
*.egg-info/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# Virtual environments
|
|
11
|
+
.venv/
|
|
12
|
+
|
|
13
|
+
# IDE
|
|
14
|
+
.vscode/
|
|
15
|
+
|
|
16
|
+
# OS
|
|
17
|
+
.DS_Store
|
|
18
|
+
Thumbs.db
|
|
19
|
+
|
|
20
|
+
# Testing
|
|
21
|
+
.import_linter_cache/
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.ruff_cache/
|
|
24
|
+
.mypy_cache/
|
|
25
|
+
.lisa-tmp/
|
|
26
|
+
coverage/
|
|
27
|
+
.coverage
|
|
28
|
+
.coverage.*
|
|
29
|
+
htmlcov/
|
|
30
|
+
|
|
31
|
+
# Outputs
|
|
32
|
+
artifacts/
|
|
33
|
+
.e2e/
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Lisa SDK Package
|
|
3
|
+
applyTo: "sdk/**/*.py"
|
|
4
|
+
description: Package-specific guidance for SDK work.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# SDK package guidance
|
|
8
|
+
|
|
9
|
+
- Keep SDK helpers generic and dependency-light. They should stay reusable rather than embedding host-specific behavior.
|
|
10
|
+
- Preserve the existing boundary between SDK domain logic and first-party MCP transport facades.
|
|
11
|
+
- Keep file and fetch primitives explicit about sandboxing and policy rather than relying on callers to enforce it.
|
|
12
|
+
- Prefer simple, import-safe helpers with explicit parameters over environment-driven behavior.
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
# Operation Authoring Guide
|
|
2
|
+
|
|
3
|
+
This guide walks through creating operations for the Lisa Harness. For the conceptual
|
|
4
|
+
overview and lifecycle details see [kernel dispatch contracts](../kernel/README.md#dispatch-and-operation-contracts).
|
|
5
|
+
|
|
6
|
+
## Choose your operation kind
|
|
7
|
+
|
|
8
|
+
For the package layout and a complete local greeting pack, start with
|
|
9
|
+
[pack authoring](../docs/PACK_AUTHORING_GUIDE.md). This guide covers the
|
|
10
|
+
operation-specific contract.
|
|
11
|
+
|
|
12
|
+
| Use case | Kind | Format |
|
|
13
|
+
| --- | --- | --- |
|
|
14
|
+
| Deterministic Python logic — read state, validate, format | **Runtime** | `.yml` + `.py` |
|
|
15
|
+
| LLM task — synthesize, research, reflect, guided tool use | **Declarative (prompt)** | `.operation.md` (optionally + `.py` response model) |
|
|
16
|
+
|
|
17
|
+
When in doubt, start declarative. Reach for a runtime operation only when branching,
|
|
18
|
+
validation, retry logic, or non-LLM data shaping makes a markdown instruction
|
|
19
|
+
impractical to maintain.
|
|
20
|
+
|
|
21
|
+
## Prerequisites
|
|
22
|
+
|
|
23
|
+
Operations live in `operations/` subdirectories within any content root. A content root
|
|
24
|
+
is any directory registered via the `lisa.extensions.content_roots` entry point in your
|
|
25
|
+
pack's `pyproject.toml`, or via the `SDK_CONTENT_ROOTS` environment variable. The
|
|
26
|
+
`lisa-catalog` MCP server discovers the catalog; you never register individual files.
|
|
27
|
+
|
|
28
|
+
```toml
|
|
29
|
+
# pyproject.toml
|
|
30
|
+
[project.entry-points."lisa.extensions.content_roots"]
|
|
31
|
+
my_pack = "my_pack:get_content_root"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
# my_pack/__init__.py
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_content_root() -> Path:
|
|
40
|
+
return Path(__file__).parent
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Runtime operation
|
|
44
|
+
|
|
45
|
+
### Directory shape
|
|
46
|
+
|
|
47
|
+
```plaintext
|
|
48
|
+
my_pack/
|
|
49
|
+
operations/
|
|
50
|
+
my_feature/
|
|
51
|
+
my_operation.yml ← required metadata
|
|
52
|
+
my_operation.py ← required implementation
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Metadata file (`.yml`)
|
|
56
|
+
|
|
57
|
+
```yaml
|
|
58
|
+
operation_id: my_operation
|
|
59
|
+
description: One clear sentence describing what this operation does.
|
|
60
|
+
mode_allowlist: [my_mode]
|
|
61
|
+
tool_ids: [] # list tools this operation may call; empty is fine
|
|
62
|
+
timeout_seconds: 30 # default 30; 0 disables timeout (tool/operation self-bounds)
|
|
63
|
+
max_calls_per_turn: 1 # default 0 (unlimited)
|
|
64
|
+
preferred_model_id: default # optional; omit to inherit mode default
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Required:** `operation_id`, `description`.
|
|
68
|
+
|
|
69
|
+
**Optional:** `mode_allowlist` (default allows all modes), `tool_ids`, `timeout_seconds`,
|
|
70
|
+
`max_calls_per_turn`, `preferred_model_id`.
|
|
71
|
+
|
|
72
|
+
`mode_allowlist` can also be omitted — the operation will then be allowed for every
|
|
73
|
+
mode unless `tools_authoritative: true` is set on the mode and the operation is not
|
|
74
|
+
listed there.
|
|
75
|
+
|
|
76
|
+
### Implementation (`.py`)
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from kernel.models import OperationExecutionContext, OperationResult
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
async def execute(ctx: OperationExecutionContext) -> OperationResult:
|
|
83
|
+
"""Execute the operation and return a structured result."""
|
|
84
|
+
return OperationResult(output_text=f"Received: {ctx.query}")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The module must export `async def execute(ctx: OperationExecutionContext) -> OperationResult`.
|
|
88
|
+
No other exports are required.
|
|
89
|
+
|
|
90
|
+
### Optional `Parameters` class
|
|
91
|
+
|
|
92
|
+
If you want the model to supply structured arguments when calling the operation as a
|
|
93
|
+
tool, export a Pydantic `Parameters` class. The catalog loader detects it automatically
|
|
94
|
+
and builds the function-tool schema from it.
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from pydantic import BaseModel, Field
|
|
98
|
+
from kernel.models import OperationExecutionContext, OperationResult
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class Parameters(BaseModel):
|
|
102
|
+
limit: int = Field(default=10, ge=1, le=100, description="Max items to return.")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
async def execute(ctx: OperationExecutionContext) -> OperationResult:
|
|
106
|
+
limit = int(ctx.params.get("limit", 10))
|
|
107
|
+
# use limit ...
|
|
108
|
+
return OperationResult(output_text=f"Returning up to {limit} items.")
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Calling tools from a runtime operation
|
|
112
|
+
|
|
113
|
+
List the tools in `tool_ids` and call them via `ctx.host.execute_tool`:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from kernel.models import OperationExecutionContext, OperationResult, ToolExecutionContext
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
async def execute(ctx: OperationExecutionContext) -> OperationResult:
|
|
120
|
+
tool_ctx = ToolExecutionContext(
|
|
121
|
+
tool_id="read_file",
|
|
122
|
+
workspace_id=ctx.workspace_id,
|
|
123
|
+
user_id=ctx.user_id,
|
|
124
|
+
query=ctx.query,
|
|
125
|
+
parameters={"path": "status.txt"},
|
|
126
|
+
)
|
|
127
|
+
result = await ctx.host.execute_tool("read_file", tool_ctx)
|
|
128
|
+
return OperationResult(output_text=result.output_text)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Matching YAML:
|
|
132
|
+
|
|
133
|
+
```yaml
|
|
134
|
+
operation_id: read_status_file
|
|
135
|
+
description: Read a status file from storage.
|
|
136
|
+
tool_ids: [read_file]
|
|
137
|
+
mode_allowlist: [my_mode]
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### The `OperationHost` typed seam
|
|
141
|
+
|
|
142
|
+
Operations interact with the host (LLM calls, tool execution, authored
|
|
143
|
+
content, model resolution) through a single typed seam: `ctx.host`. The
|
|
144
|
+
`OperationHost` Protocol (defined in `kernel/seams/protocols.py`) declares
|
|
145
|
+
the methods operations may call:
|
|
146
|
+
|
|
147
|
+
| Method | Purpose |
|
|
148
|
+
| --- | --- |
|
|
149
|
+
| `await ctx.host.complete(messages=..., model_def=..., max_tokens=..., temperature=..., tools=..., response_format=...)` | Call the host's LLM. Returns a `CompletionResult` with `.content`, `.tool_calls`, `.finish_reason`, and `.truncated`. |
|
|
150
|
+
| `ctx.host.resolve_model(model_id)` | Resolve a model id to a `ModelRef` (provider, model_id, capabilities). |
|
|
151
|
+
| `ctx.host.read_authored_text(path)` | Load authored Markdown content shipped as package data. Raises on missing. |
|
|
152
|
+
| `await ctx.host.execute_tool(tool_id, tool_ctx)` | Execute a tool by id. Returns a `ToolResult`. |
|
|
153
|
+
| `await ctx.host.execute_operation(op_id, op_ctx)` | Execute a sub-operation. |
|
|
154
|
+
| `await ctx.host.generate_image(...)` | Generate an image (when wired). |
|
|
155
|
+
| `ctx.host.introspect(...)` | Introspect live runtime state (when wired). |
|
|
156
|
+
|
|
157
|
+
The `truncated` field on the completion result is the **provider-owned
|
|
158
|
+
truncation signal**: `True` = the model stopped mid-output (hit token
|
|
159
|
+
budget), `False` = clean stop, `None` = adapter cannot tell. Operations
|
|
160
|
+
that synthesize long-form output use this to decide whether to issue a
|
|
161
|
+
continuation pass. This replaces the old heuristic of guessing from
|
|
162
|
+
`finish_reason` strings.
|
|
163
|
+
|
|
164
|
+
### Reading cached state
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from kernel.models import OperationExecutionContext, OperationResult, UnifiedState
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
async def execute(ctx: OperationExecutionContext) -> OperationResult:
|
|
171
|
+
state: UnifiedState | None = ctx.state_provider() if ctx.state_provider else None
|
|
172
|
+
if state is None:
|
|
173
|
+
return OperationResult(output_text="No cached state available.")
|
|
174
|
+
items = [item for item in state.context_items if item.source == "my_service"]
|
|
175
|
+
if not items:
|
|
176
|
+
return OperationResult(output_text="No evidence yet.")
|
|
177
|
+
return OperationResult(output_text="\n".join(item.body for item in items))
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Declarative (prompt) operation
|
|
181
|
+
|
|
182
|
+
### Directory shape
|
|
183
|
+
|
|
184
|
+
```plaintext
|
|
185
|
+
my_pack/
|
|
186
|
+
operations/
|
|
187
|
+
research/
|
|
188
|
+
briefing.operation.md ← metadata + instructions in one file
|
|
189
|
+
briefing.py ← optional Pydantic response model (no execute())
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Markdown file (`.operation.md`)
|
|
193
|
+
|
|
194
|
+
```markdown
|
|
195
|
+
---
|
|
196
|
+
operation_id: research_briefing
|
|
197
|
+
description: Produce a grounded research summary from web results.
|
|
198
|
+
tool_ids: [web_search]
|
|
199
|
+
mode_allowlist: [research]
|
|
200
|
+
timeout_seconds: 60
|
|
201
|
+
max_calls_per_turn: 1
|
|
202
|
+
completion_max_tokens: 2000
|
|
203
|
+
completion_temperature: 0.2
|
|
204
|
+
response_format_model_path: my_pack.operations.research.briefing.BriefingOutput
|
|
205
|
+
response_output_text_field: summary
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
# Research Briefing
|
|
209
|
+
|
|
210
|
+
You are a grounded research analyst. Use `web_search` to gather evidence, then
|
|
211
|
+
synthesize only from the tool results.
|
|
212
|
+
|
|
213
|
+
## Rules
|
|
214
|
+
|
|
215
|
+
1. Run 2–3 independent searches.
|
|
216
|
+
2. Never invent URLs or facts.
|
|
217
|
+
3. If no grounded results are available, set `summary` to the search error and leave
|
|
218
|
+
other fields empty.
|
|
219
|
+
|
|
220
|
+
## Output
|
|
221
|
+
|
|
222
|
+
Return one JSON object matching the declared schema.
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Required front-matter fields:** `operation_id`, `description`.
|
|
226
|
+
|
|
227
|
+
**Optional fields:**
|
|
228
|
+
|
|
229
|
+
| Field | Default | Purpose |
|
|
230
|
+
| --- | --- | --- |
|
|
231
|
+
| `tool_ids` | `[]` | Tools the model may call in the instruction loop |
|
|
232
|
+
| `mode_allowlist` | (all modes) | Modes allowed to invoke this operation |
|
|
233
|
+
| `timeout_seconds` | `60` | Wall-clock limit for the full instruction loop |
|
|
234
|
+
| `max_calls_per_turn` | `0` | Max times this operation can be called in one turn (`0` = unlimited) |
|
|
235
|
+
| `completion_max_tokens` | `declarative_completion_max_tokens` setting (default `1500`) | Token budget for the model completion |
|
|
236
|
+
| `completion_temperature` | `declarative_completion_temperature` setting (default `0.2`) | Temperature for the model completion |
|
|
237
|
+
| `preferred_model_id` | (mode default) | Model alias to use for this operation |
|
|
238
|
+
| `response_format_model_path` | (none) | Dotted path to a Pydantic response model class |
|
|
239
|
+
| `response_output_text_field` | `"output_text"` | Field name to extract as `OperationResult.output_text` |
|
|
240
|
+
| `response_output_json_passthrough` | `{}` | Dotted-path mappings to copy from last tool output into result |
|
|
241
|
+
|
|
242
|
+
### Optional response-format model (`.py`)
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
from pydantic import BaseModel, Field
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class BriefingOutput(BaseModel):
|
|
249
|
+
summary: str = Field(default="", description="Full markdown briefing.")
|
|
250
|
+
sources: list[str] = Field(default_factory=list, description="Grounded URLs from tool results.")
|
|
251
|
+
key_points: list[str] = Field(default_factory=list, description="Bullet-point highlights.")
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
When `response_format_model_path` is set, the declarative runtime:
|
|
255
|
+
|
|
256
|
+
1. Passes the model class to `ctx.host.complete` as `response_format`.
|
|
257
|
+
2. On a parse failure or a provider rejection (`structured_output_unsupported`), appends
|
|
258
|
+
a JSON-only retry instruction and retries without the `response_format` parameter.
|
|
259
|
+
3. Extracts `response_output_text_field` from the parsed model for `OperationResult.output_text`.
|
|
260
|
+
|
|
261
|
+
If you omit the response model, the operation returns the model's raw text.
|
|
262
|
+
|
|
263
|
+
## Binding operations to a mode
|
|
264
|
+
|
|
265
|
+
Operations are invoked by the turn runner when a mode declares them in its
|
|
266
|
+
`_mode.md` front matter:
|
|
267
|
+
|
|
268
|
+
```yaml
|
|
269
|
+
---
|
|
270
|
+
mode_id: my_mode
|
|
271
|
+
mode_kind: chat
|
|
272
|
+
entry_operation_ids: [load_context] # run once on mode entry
|
|
273
|
+
pre_turn_operation_ids: [research_briefing] # run before every model completion
|
|
274
|
+
allowed_operation_ids: [recall, summarize] # model can call these as tools
|
|
275
|
+
post_turn_operation_ids: [synthesize_notes] # run after every turn
|
|
276
|
+
---
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Listing an operation in any of these four lists automatically grants it permission for
|
|
280
|
+
the mode, regardless of the operation's own `mode_allowlist`. Mode-owned bindings are
|
|
281
|
+
the authoritative source; `mode_allowlist` in the operation's metadata is a secondary
|
|
282
|
+
grant.
|
|
283
|
+
|
|
284
|
+
## Discovery rules and precedence
|
|
285
|
+
|
|
286
|
+
- `sdk.content.build_operation_catalog_json` scans `operations/` recursively.
|
|
287
|
+
- Markdown operations (`.operation.md`) are loaded first and take precedence.
|
|
288
|
+
- If a directory has both `my_op.operation.md` and `my_op.py`, the `.py` file is
|
|
289
|
+
treated as the response-format model only — its `execute` function (if any) is
|
|
290
|
+
ignored.
|
|
291
|
+
- Python runtime operations must have a sibling `.yml` file with matching `operation_id`
|
|
292
|
+
or the catalog loader skips them with a warning.
|
|
293
|
+
- Module paths for Python modules are inferred automatically from their location
|
|
294
|
+
relative to the package root. Use `module_path` in the `.yml` only when auto-inference
|
|
295
|
+
is wrong.
|
|
296
|
+
|
|
297
|
+
## Testing
|
|
298
|
+
|
|
299
|
+
### Import boundary test (required)
|
|
300
|
+
|
|
301
|
+
Every pack must verify it imports cleanly without private app modules:
|
|
302
|
+
|
|
303
|
+
```python
|
|
304
|
+
# tests/test_import_boundaries.py
|
|
305
|
+
import importlib
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def test_pack_imports_cleanly() -> None:
|
|
309
|
+
importlib.import_module("my_pack")
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Catalog discovery test (recommended)
|
|
313
|
+
|
|
314
|
+
```python
|
|
315
|
+
# tests/test_catalog.py
|
|
316
|
+
from pathlib import Path
|
|
317
|
+
from sdk.content import build_operation_catalog_json
|
|
318
|
+
import json
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def test_my_operation_discovered() -> None:
|
|
322
|
+
import my_pack
|
|
323
|
+
|
|
324
|
+
root = my_pack.get_content_root() / "operations"
|
|
325
|
+
catalog = json.loads(build_operation_catalog_json([root]))
|
|
326
|
+
assert "my_operation" in catalog
|
|
327
|
+
op = catalog["my_operation"]
|
|
328
|
+
assert op["description"]
|
|
329
|
+
assert "my_mode" in op["mode_allowlist"]
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def test_tool_ids_declared() -> None:
|
|
333
|
+
import my_pack
|
|
334
|
+
|
|
335
|
+
root = my_pack.get_content_root() / "operations"
|
|
336
|
+
catalog = json.loads(build_operation_catalog_json([root]))
|
|
337
|
+
assert catalog["read_status_file"]["tool_ids"] == ["read_file"]
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Validate against live modes
|
|
341
|
+
|
|
342
|
+
After source tests pass, install/activate the pack through a compatible
|
|
343
|
+
host and inspect `/modes` and `/operations`, then invoke its mode command.
|
|
344
|
+
The framework workspace does not install `lisa`; use the external
|
|
345
|
+
[CLI reference](https://github.com/Lisa-Harness/lisa-cli) for its command
|
|
346
|
+
syntax. `SDK_CONTENT_ROOTS` discovers content but does not make arbitrary
|
|
347
|
+
Python modules importable.
|
|
348
|
+
|
|
349
|
+
## Pre-merge checklist
|
|
350
|
+
|
|
351
|
+
- `packstest.catalog.validate_pack(<pack-path>)` passes from source (entry points load, every tool/operation YAML parses, all implementation modules import in-process).
|
|
352
|
+
- `operation_id` is unique across all packs you intend to load simultaneously.
|
|
353
|
+
- `description` is a non-empty one-line summary.
|
|
354
|
+
- Every entry in `tool_ids` is registered in the tool catalog.
|
|
355
|
+
- `mode_allowlist` lists at least one mode, or the operation is intentionally global.
|
|
356
|
+
- For declarative operations: the front-matter body is non-empty and
|
|
357
|
+
`response_format_model_path` resolves to a real Pydantic class.
|
|
358
|
+
- The pack imports cleanly with no host or private app imports.
|
|
359
|
+
- Catalog discovery test passes without live services.
|
|
360
|
+
|
|
361
|
+
## Common errors
|
|
362
|
+
|
|
363
|
+
| Error | Cause | Fix |
|
|
364
|
+
| --- | --- | --- |
|
|
365
|
+
| `OperationValidationError: … missing execute(ctx)` | Python module has no `execute` function | Add `async def execute(ctx)` |
|
|
366
|
+
| `OperationValidationError: … references unknown tool 'X'` | `tool_ids` entry not in tool catalog | Register the tool in your pack's `tools/` dir or remove the entry |
|
|
367
|
+
| `OperationExecutionError: … is not allowed for mode 'Y'` | Mode doesn't bind the operation and `mode_allowlist` doesn't include it | Add operation to `allowed_operation_ids` in the mode's `_mode.md` |
|
|
368
|
+
| `OperationExecutionError: cannot call undeclared tool 'X'` | Declarative operation called a tool not in `tool_ids` | Add the tool to `tool_ids` in the front matter |
|
|
369
|
+
| `CatalogCollisionError: duplicate operation_id 'X'` | Two packs define the same `operation_id` | Use `LISA_SESSION_PROVIDER_SELECTIONS` or rename one operation |
|
|
370
|
+
| `validate_pack`: `module … not importable` | Tool/operation module missing or unimportable from the content root | Add the missing `__init__.py` (every package level) or fix the `module_path` |
|
|
371
|
+
| `validate_pack`: `provider id unresolved` | Tool YAML has no `pack_id`/provider and the content root has no readable `pyproject.toml` | Set `tool_provider_id`/`pack_id` in the YAML; keep the distribution `pyproject.toml` at the pack root |
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: lisa-harness-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lisa SDK — reusable operations, modes, signal connector shims, and domain models for Lisa-powered bots
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: cel-python>=0.5.0
|
|
7
|
+
Requires-Dist: diskcache>=5.6
|
|
8
|
+
Requires-Dist: httpx>=0.27.0
|
|
9
|
+
Requires-Dist: lisa-harness-kernel
|
|
10
|
+
Requires-Dist: openai>=1.59.0
|
|
11
|
+
Requires-Dist: pydantic>=2.13.4
|
|
12
|
+
Requires-Dist: sqlite-vec>=0.1.9
|
|
13
|
+
Requires-Dist: tiktoken>=0.12.0
|
|
14
|
+
Requires-Dist: wcmatch>=8.5
|
|
15
|
+
Provides-Extra: bedrock
|
|
16
|
+
Requires-Dist: boto3>=1.34; extra == 'bedrock'
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
19
|
+
Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest-mock>=3.14.0; extra == 'dev'
|
|
21
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# SDK
|
|
2
|
+
|
|
3
|
+
`lisa-harness-sdk` (`sdk`) is the reusable content-discovery, customization-parsing, and repository-facing helper layer of the Lisa Harness. It sits between the runtime-neutral `kernel` and the concrete runtime that packs compose: the kernel owns contracts and the turn loop, the SDK owns the catalog scanners, model router, provider gateways, and the memory/thread/guard/storage/fetch primitives that first-party MCP servers expose.
|
|
4
|
+
|
|
5
|
+
## Design Philosophy
|
|
6
|
+
|
|
7
|
+
The SDK holds **domain logic for everything the first-party MCP servers expose**, plus the pluggable runtime backends (providers, routers) that hosts discover through entry points. `mcp_server` stays a thin transport facade over these modules.
|
|
8
|
+
|
|
9
|
+
Key principles:
|
|
10
|
+
|
|
11
|
+
- **Generic and dependency-light**: SDK abstractions must work for any runtime. The SDK may import from `kernel`, never from `runtime`.
|
|
12
|
+
- **Import-safe public helpers**: no registration, environment reads, or I/O at module import time. Resolved configuration is accepted explicitly.
|
|
13
|
+
- **Server boundary**: file I/O goes through the storage sandbox (relative paths only, deny-glob policy); SSRF protections and the response-size ceiling in the fetch primitive are non-bypassable. Environment translation belongs in host or tool adapters, not in the primitives.
|
|
14
|
+
- **Namespace package**: `sdk` is a PEP 420 namespace shared with the bundled packs under `sdk/packs/`, which ship their own wheels. The `sdk` content root is exported from [`src/sdk/_content_root.py`](src/sdk/_content_root.py).
|
|
15
|
+
|
|
16
|
+
See [architecture](../docs/BOUNDARIES.md), [memory](../docs/MEMORY_ARCHITECTURE.md),
|
|
17
|
+
[storage](../docs/STORAGE.md), and the [documentation index](../docs/README.md)
|
|
18
|
+
for cross-package contracts.
|
|
19
|
+
|
|
20
|
+
## Module Map
|
|
21
|
+
|
|
22
|
+
### Catalog, Content & Context
|
|
23
|
+
|
|
24
|
+
| Module | Responsibility |
|
|
25
|
+
| --- | --- |
|
|
26
|
+
| `content/` | Content catalog and authored-text reading: mode/operation/tool catalog JSON builders, content-root resolution, `ContentReader`, path safety, Copilot customization discovery/parsing (applyTo globbing). Powers the `lisa-content` and `lisa-catalog` MCP servers. |
|
|
27
|
+
| `context/` | Entry-point-aware extension item building: discovers `lisa.extensions.context_items` entry points and feeds extension evidence into the session-owned context mapper via `ContextSeams.extension_item_builder`. |
|
|
28
|
+
| `config.py` | Public runtime `Settings` (pydantic-settings) for packs/extensions — deliberately narrower than root app config, with owned-source filtering so packs only consume env keys they declare. |
|
|
29
|
+
| `pack_settings.py` | `OwnedSettingsSource` / `build_owned_settings_sources`; importable by external packs without depending on `sdk_packs`. |
|
|
30
|
+
| `layout.py` | Single source of truth for the canonical data-dir categories (`config/`, `state/`, `data/`, `cache/`, `logs/`, `run/`) shared by hosts, SDK, MCP servers, and packs. |
|
|
31
|
+
|
|
32
|
+
### State & Memory Primitives
|
|
33
|
+
|
|
34
|
+
| Module | Responsibility |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| `memory/` | SQLite-backed memory store library (vec0 embeddings, backups). The `lisa-memory` MCP server wraps it. |
|
|
37
|
+
| `thread/` | Lossless, segmented conversation-thread store (`ThreadStore`): three-tier layout (active/sealed segments + sparse index) with zstd sidecars and redaction policy. Owns the read path; the write path stays in `runtime.thread`. |
|
|
38
|
+
| `storage/` | Sandboxed file I/O: `StorageBackend` owns the files sandbox, deny-glob policy, and model-manifest read/write. The `lisa-storage` MCP server wraps it. |
|
|
39
|
+
| `workproducts.py` | Typed, pack-neutral `WorkProduct` contract plus `WorkProductStore` protocol — durable, approvable units of work shared across packs (cross-pack imports are forbidden, so the contract sits on neutral SDK ground). The host injects the file-based implementation. |
|
|
40
|
+
|
|
41
|
+
### Safety & Fetch
|
|
42
|
+
|
|
43
|
+
| Module | Responsibility |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `guard/` | Content-safety validation backends: `GuardBackend` protocol, default `PassthroughGuardBackend`, result formatting, blocked-check audit logging. The `lisa-guard` MCP server owns transport. |
|
|
46
|
+
| `mcp_host/` | Pure config construction for first-party MCP servers (`build_content_mcp_config`, `build_catalog_mcp_config`). Pool lifecycle is deliberately host-owned because anyio cancel scopes bind to the host's event loop. |
|
|
47
|
+
|
|
48
|
+
### Pluggable Runtime Backends
|
|
49
|
+
|
|
50
|
+
| Module | Responsibility |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| `providers/` | Reference `ProviderGatewayProvider` implementations for OpenAI, Azure OpenAI, Anthropic, Google Gemini, and AWS Bedrock, registered via `lisa.extensions.provider_gateways`. Explicitly not a pack — a pluggable runtime backend. |
|
|
53
|
+
| `router/` | The built-in `lisa-router`: a pure-Python CEL/rules engine that selects a model per turn-loop iteration from the manifest's `router.rules` block with zero model calls, registered via `lisa.extensions.model_routers`. See [`src/sdk/router/README.md`](src/sdk/router/README.md). |
|
|
54
|
+
|
|
55
|
+
### Shared Infrastructure
|
|
56
|
+
|
|
57
|
+
| Module | Responsibility |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| `logging_setup.py` | Shared stdlib-logging infrastructure for all hosts and first-party MCP servers: `JSONFormatter`, `RateLimitFilter`, `configure_logging`, `emit_structured_event`. Stdlib-only so subprocesses need not import the kernel. |
|
|
60
|
+
| `workspace.py` | Single shared `resolve_workspace_root` with explicit strictness (`require_git` knob) so packs no longer derive the user's project root independently. |
|
|
61
|
+
|
|
62
|
+
## Bundled Packs
|
|
63
|
+
|
|
64
|
+
The `sdk/packs/` subtree contains the bundled reference packs — `core`, `memory`, `introspect`, `recovery` — which ship as their own distributions and are excluded from the `lisa-harness-sdk` wheel. For pack structure, mode front matter, operations, signal connectors, and the testing workflow, see:
|
|
65
|
+
|
|
66
|
+
- [docs/PACK_AUTHORING_GUIDE.md](../docs/PACK_AUTHORING_GUIDE.md) — pack anatomy, mode files, extension entry points, and the hello-world walkthrough.
|
|
67
|
+
- [AUTHORING.md](AUTHORING.md) — the operation authoring deep-dive (runtime vs. declarative, metadata fields, the `execute(ctx)` contract).
|
|
68
|
+
- The [lisa-packs](https://github.com/Lisa-Harness/lisa-packs) repo — the public external packs, their installation, and the per-pack submodule workflow for authoring real integration packs.
|
|
69
|
+
|
|
70
|
+
## Testing
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
uv run pytest sdk/tests sdk/src/sdk/context/tests sdk/src/sdk/guard/tests sdk/src/sdk/memory/tests sdk/src/sdk/storage/tests -m "not live" -q
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Run from the workspace root; this selects SDK tests, not bundled pack suites.
|
|
77
|
+
See [testing](../docs/TESTING.md) for the complete CI selection.
|
|
78
|
+
|
|
79
|
+
Markers: `live` (hits real external signal or provider APIs), `integration` (multi-module in-process; may require Azurite on localhost:10000), `eval` (deterministic agent regression evals — no model calls, no I/O). Pack validation itself belongs to the `packstest` harness; see [`../packstest/README.md`](../packstest/README.md).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: SDK Packs
|
|
3
|
+
applyTo: "sdk/packs/**/*.py"
|
|
4
|
+
description: Package-specific guidance for pack development.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Pack development guidance
|
|
8
|
+
|
|
9
|
+
- Keep packs self-contained and host-neutral. Prefer thin runtime handlers over embedding orchestration concerns.
|
|
10
|
+
- Preserve the existing discovery and registration pattern for operations and tools.
|
|
11
|
+
- Keep pack content and runtime behavior aligned so the catalog can discover operations without side effects.
|
|
12
|
+
- Avoid cross-pack imports; promote shared helpers into the SDK when needed.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Bundled reference packs
|
|
2
|
+
|
|
3
|
+
These are independently packaged distributions in the shared `sdk.packs`
|
|
4
|
+
namespace, not submodules included in the SDK wheel.
|
|
5
|
+
|
|
6
|
+
| Pack | Role | Activation |
|
|
7
|
+
| --- | --- | --- |
|
|
8
|
+
| [core](core/) | Default, planner, and explainer modes; delegation/planning operations, fetch tool, shared prompts, default session | Host-mandatory |
|
|
9
|
+
| [introspect](introspect/) | Operational self-model and reflective assessment | Host-mandatory |
|
|
10
|
+
| [memory](memory/) | Scoped recall/learning operations, memory backend manifest, consolidation/mining slice | Optional |
|
|
11
|
+
| [recovery](recovery/src/sdk/packs/recovery/README.md) | Inspect, retry, skip/cancel stalled dispatches and resume yielded turns | Optional |
|
|
12
|
+
|
|
13
|
+
All four are runtime dependencies. That does not make every optional pack
|
|
14
|
+
active in every session. Host policy, pack lock state, and the session plan
|
|
15
|
+
control activation. Use `/pack`, `/modes`, `/operations`, and `/tools` to
|
|
16
|
+
inspect the active catalog.
|
|
17
|
+
|
|
18
|
+
## Authoring and ownership
|
|
19
|
+
|
|
20
|
+
Packs export content roots and other extension entry points. They provide
|
|
21
|
+
workflow and application capability, not the host's transport or durable
|
|
22
|
+
service implementation. New packs should depend on kernel/SDK contracts
|
|
23
|
+
and injected services, not runtime internals or another pack.
|
|
24
|
+
|
|
25
|
+
- [Pack authoring](../../docs/PACK_AUTHORING_GUIDE.md)
|
|
26
|
+
- [Operation authoring](../AUTHORING.md)
|
|
27
|
+
- [Session selection](../../docs/SESSION_CONTEXT_AUTHORING_GUIDE.md)
|
|
28
|
+
- [Memory architecture](../../docs/MEMORY_ARCHITECTURE.md)
|
|
29
|
+
- [External pack registry](https://github.com/Lisa-Harness/lisa-packs)
|
|
30
|
+
|
|
31
|
+
Bundled pack prompts/modes are part of behavior and are held to the same
|
|
32
|
+
[test assurances](../../docs/CODING_WORKFLOW.md#test-assurances) as code.
|
|
33
|
+
Editing a prompt is not automatically a documentation-only change.
|
|
34
|
+
|
|
35
|
+
## Verification
|
|
36
|
+
|
|
37
|
+
From the workspace root:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
uv run pytest sdk/packs/core/tests sdk/packs/memory/tests sdk/packs/recovery/tests -m "not live" -q
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Run [packstest](../../packstest/README.md) from source before testing an
|
|
44
|
+
installed wheel. Bundled packs use its
|
|
45
|
+
[explicit-root scan](../../packstest/README.md#source-layout-packs);
|
|
46
|
+
warning-only root discovery is not catalog coverage. Workflows also need opt-in
|
|
47
|
+
[whole-runtime scenarios](../../e2etest/README.md).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[tool.hatch.build.targets.wheel]
|
|
6
|
+
# sdk.packs.core lives under the shared sdk namespace; hatchling includes
|
|
7
|
+
# all files in the package dir by default (no package-data globs needed).
|
|
8
|
+
packages = ["src/sdk"]
|
|
9
|
+
only-include = ["src/sdk/packs/core"]
|
|
10
|
+
|
|
11
|
+
[project]
|
|
12
|
+
name = "lisa-pack-core"
|
|
13
|
+
version = "0.1.0"
|
|
14
|
+
description = "Lisa core pack - baseline modes, operations, and authored content"
|
|
15
|
+
requires-python = ">=3.11"
|
|
16
|
+
|
|
17
|
+
[project.optional-dependencies]
|
|
18
|
+
dev = [
|
|
19
|
+
"pytest>=8.0.0",
|
|
20
|
+
"pytest-asyncio>=0.23.0",
|
|
21
|
+
"pytest-cov>=6.0.0",
|
|
22
|
+
"pytest-mock>=3.14.0",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[tool.pytest.ini_options]
|
|
26
|
+
pythonpath = ["src"]
|
|
27
|
+
testpaths = ["tests"]
|
|
28
|
+
markers = [
|
|
29
|
+
"live: hits real external signal or provider APIs and prints a tester-visible summary",
|
|
30
|
+
"integration: multi-module in-process test; may require Azurite on localhost:10000",
|
|
31
|
+
"eval: deterministic agent regression evals (no model calls, no I/O)",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.entry-points."lisa.extensions.content_roots"]
|
|
35
|
+
core = "sdk.packs.core:get_content_root"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Core baseline pack for Lisa.
|
|
2
|
+
|
|
3
|
+
Contains the shipped baseline modes, authored markdown assets, and reusable operations
|
|
4
|
+
that the host runtime loads through pack discovery.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_content_root() -> Path:
|
|
13
|
+
"""Return the content root directory for this pack."""
|
|
14
|
+
return Path(__file__).parent
|
lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/prompts/sub_turn/delegate_contract.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Delegate-child sub-turn contract body
|
|
2
|
+
|
|
3
|
+
Subroutine child: success REQUIRES calling at least one of: [{tools_list}].
|
|
4
|
+
Read-only calls do NOT count. If the budget exhausts without a write, return
|
|
5
|
+
status=no_tools so your parent can decide — do not claim success.
|
|
6
|
+
|
|
7
|
+
{remaining_iterations} iterations remain to produce a write; reads are
|
|
8
|
+
scaffolding, not progress.
|
lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/prompts/sub_turn/fork_contract.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Fork-child sub-turn contract body
|
|
2
|
+
|
|
3
|
+
Fork child: success REQUIRES calling at least one of: [{tools_list}]. Read-only
|
|
4
|
+
calls do NOT count. If the budget exhausts without a write, your output is
|
|
5
|
+
discarded as unverified — do not claim success.
|
|
6
|
+
|
|
7
|
+
{remaining_iterations} iterations remain to produce a write; reads are
|
|
8
|
+
scaffolding, not progress.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Sub-turn midpoint write-nudge body
|
|
2
|
+
|
|
3
|
+
You are past the midpoint of your budget with zero writes. Call one of:
|
|
4
|
+
[{action_list}] eminently to complete your work, or call `yield` to hand back
|
|
5
|
+
honestly. Continuing to read without writing will result in your output being
|
|
6
|
+
discarded.
|