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.
Files changed (181) hide show
  1. lisa_harness_sdk-0.1.0/.gitignore +33 -0
  2. lisa_harness_sdk-0.1.0/.instructions.md +12 -0
  3. lisa_harness_sdk-0.1.0/AUTHORING.md +371 -0
  4. lisa_harness_sdk-0.1.0/PKG-INFO +21 -0
  5. lisa_harness_sdk-0.1.0/README.md +79 -0
  6. lisa_harness_sdk-0.1.0/packs/.instructions.md +12 -0
  7. lisa_harness_sdk-0.1.0/packs/README.md +47 -0
  8. lisa_harness_sdk-0.1.0/packs/core/pyproject.toml +35 -0
  9. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/__init__.py +14 -0
  10. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/prompts/sub_turn/budget_exhausted.md +3 -0
  11. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/prompts/sub_turn/delegate_contract.md +8 -0
  12. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/prompts/sub_turn/fork_contract.md +8 -0
  13. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/prompts/sub_turn/midpoint_write_nudge.md +6 -0
  14. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/prompts/sub_turn/no_write_warning.md +4 -0
  15. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/prompts/system-prompt.md +9 -0
  16. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/prompts/thread_manifest_hint.md +37 -0
  17. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/content/redaction_patterns.yml +46 -0
  18. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/modes/default/_mode.md +17 -0
  19. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/modes/explainer/_mode.md +43 -0
  20. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/modes/planner/_mode.md +41 -0
  21. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/__init__.py +1 -0
  22. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/delegation/__init__.py +1 -0
  23. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/delegation/delegate.md +61 -0
  24. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/delegation/delegate.py +198 -0
  25. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/delegation/delegate.yml +14 -0
  26. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/delegation/fork.md +73 -0
  27. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/delegation/fork.py +232 -0
  28. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/delegation/fork.yml +15 -0
  29. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/planning/__init__.py +1 -0
  30. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/planning/plan_workflow.py +691 -0
  31. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/operations/planning/plan_workflow.yml +14 -0
  32. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/sessions/default.md +116 -0
  33. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/tools/__init__.py +0 -0
  34. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/tools/fetch_page/__init__.py +0 -0
  35. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/tools/fetch_page/_models.py +21 -0
  36. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/tools/fetch_page/fetch_page.py +251 -0
  37. lisa_harness_sdk-0.1.0/packs/core/src/sdk/packs/core/tools/fetch_page/fetch_page.tool.yml +9 -0
  38. lisa_harness_sdk-0.1.0/packs/core/tests/test_delegate_fn.py +186 -0
  39. lisa_harness_sdk-0.1.0/packs/core/tests/test_delegate_operation.py +195 -0
  40. lisa_harness_sdk-0.1.0/packs/core/tests/test_fetch_page_tool.py +112 -0
  41. lisa_harness_sdk-0.1.0/packs/core/tests/test_fork_fn.py +204 -0
  42. lisa_harness_sdk-0.1.0/packs/core/tests/test_fork_operation.py +381 -0
  43. lisa_harness_sdk-0.1.0/packs/core/tests/test_plan_workflow_settings.py +26 -0
  44. lisa_harness_sdk-0.1.0/packs/introspect/pyproject.toml +30 -0
  45. lisa_harness_sdk-0.1.0/packs/introspect/src/sdk/packs/introspect/__init__.py +29 -0
  46. lisa_harness_sdk-0.1.0/packs/introspect/src/sdk/packs/introspect/operations/__init__.py +1 -0
  47. lisa_harness_sdk-0.1.0/packs/introspect/src/sdk/packs/introspect/operations/self_model/__init__.py +1 -0
  48. lisa_harness_sdk-0.1.0/packs/introspect/src/sdk/packs/introspect/operations/self_model/reflect_self.operation.md +45 -0
  49. lisa_harness_sdk-0.1.0/packs/introspect/src/sdk/packs/introspect/operations/self_model/reflect_self.py +32 -0
  50. lisa_harness_sdk-0.1.0/packs/introspect/src/sdk/packs/introspect/operations/self_model/self_model.py +458 -0
  51. lisa_harness_sdk-0.1.0/packs/introspect/src/sdk/packs/introspect/operations/self_model/self_model.yml +5 -0
  52. lisa_harness_sdk-0.1.0/packs/memory/pyproject.toml +30 -0
  53. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/__init__.py +14 -0
  54. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/mcp_servers.yaml +52 -0
  55. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/__init__.py +1 -0
  56. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/consolidate_semantic.py +375 -0
  57. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/consolidate_semantic.yml +9 -0
  58. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/dispute.py +263 -0
  59. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/dispute.yml +10 -0
  60. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/mine_thread_segment.py +381 -0
  61. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/mine_thread_segment.yml +9 -0
  62. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/prompts/consolidate_semantic_system.md +10 -0
  63. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/prompts/mine_thread_segment_system.md +26 -0
  64. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/prompts/synthesize_learning_system.md +18 -0
  65. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/recall.py +210 -0
  66. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/recall.yml +10 -0
  67. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/synthesize_learning.py +430 -0
  68. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/operations/memory/synthesize_learning.yml +13 -0
  69. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/slice_provider.py +715 -0
  70. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/utils/__init__.py +1 -0
  71. lisa_harness_sdk-0.1.0/packs/memory/src/sdk/packs/memory/utils/memory_recall.py +140 -0
  72. lisa_harness_sdk-0.1.0/packs/memory/tests/test_consolidate_semantic.py +223 -0
  73. lisa_harness_sdk-0.1.0/packs/memory/tests/test_consolidate_semantic_branches.py +495 -0
  74. lisa_harness_sdk-0.1.0/packs/memory/tests/test_dispute_memory.py +433 -0
  75. lisa_harness_sdk-0.1.0/packs/memory/tests/test_distill_parse.py +73 -0
  76. lisa_harness_sdk-0.1.0/packs/memory/tests/test_distill_retry.py +180 -0
  77. lisa_harness_sdk-0.1.0/packs/memory/tests/test_memory_recall.py +370 -0
  78. lisa_harness_sdk-0.1.0/packs/memory/tests/test_mine_thread_segment.py +120 -0
  79. lisa_harness_sdk-0.1.0/packs/memory/tests/test_recall_branches.py +310 -0
  80. lisa_harness_sdk-0.1.0/packs/memory/tests/test_slice_provider.py +403 -0
  81. lisa_harness_sdk-0.1.0/packs/memory/tests/test_synthesize_learning_branches.py +437 -0
  82. lisa_harness_sdk-0.1.0/packs/memory/tests/test_synthesize_learning_tier.py +101 -0
  83. lisa_harness_sdk-0.1.0/packs/recovery/pyproject.toml +34 -0
  84. lisa_harness_sdk-0.1.0/packs/recovery/src/sdk/packs/recovery/README.md +58 -0
  85. lisa_harness_sdk-0.1.0/packs/recovery/src/sdk/packs/recovery/__init__.py +29 -0
  86. lisa_harness_sdk-0.1.0/packs/recovery/src/sdk/packs/recovery/commands.py +548 -0
  87. lisa_harness_sdk-0.1.0/packs/recovery/tests/e2e/conftest.py +1 -0
  88. lisa_harness_sdk-0.1.0/packs/recovery/tests/e2e/session_context.md +27 -0
  89. lisa_harness_sdk-0.1.0/packs/recovery/tests/e2e/skip_all.scenario.yml +29 -0
  90. lisa_harness_sdk-0.1.0/packs/recovery/tests/e2e/stall_inspect_skip.scenario.yml +29 -0
  91. lisa_harness_sdk-0.1.0/packs/recovery/tests/test_recovery_commands.py +443 -0
  92. lisa_harness_sdk-0.1.0/packs/recovery/tests/test_recovery_guards.py +512 -0
  93. lisa_harness_sdk-0.1.0/packs/recovery/tests/test_resume.py +156 -0
  94. lisa_harness_sdk-0.1.0/packs/recovery/tests/test_resume_extra.py +212 -0
  95. lisa_harness_sdk-0.1.0/pyproject.toml +75 -0
  96. lisa_harness_sdk-0.1.0/src/sdk/_content_root.py +20 -0
  97. lisa_harness_sdk-0.1.0/src/sdk/config.py +81 -0
  98. lisa_harness_sdk-0.1.0/src/sdk/content/__init__.py +117 -0
  99. lisa_harness_sdk-0.1.0/src/sdk/content/_catalog.py +1591 -0
  100. lisa_harness_sdk-0.1.0/src/sdk/content/_copilot_customizations.py +515 -0
  101. lisa_harness_sdk-0.1.0/src/sdk/content/_copilot_discovery.py +492 -0
  102. lisa_harness_sdk-0.1.0/src/sdk/content/_diagnostics.py +228 -0
  103. lisa_harness_sdk-0.1.0/src/sdk/content/_fetcher.py +517 -0
  104. lisa_harness_sdk-0.1.0/src/sdk/content/_front_matter.py +75 -0
  105. lisa_harness_sdk-0.1.0/src/sdk/content/_loader_strict.py +111 -0
  106. lisa_harness_sdk-0.1.0/src/sdk/content/_reader.py +88 -0
  107. lisa_harness_sdk-0.1.0/src/sdk/content/_roots.py +70 -0
  108. lisa_harness_sdk-0.1.0/src/sdk/content/py.typed +0 -0
  109. lisa_harness_sdk-0.1.0/src/sdk/context/__init__.py +1 -0
  110. lisa_harness_sdk-0.1.0/src/sdk/context/extension_item_builder.py +133 -0
  111. lisa_harness_sdk-0.1.0/src/sdk/context/tests/test_extension_item_builder.py +157 -0
  112. lisa_harness_sdk-0.1.0/src/sdk/guard/__init__.py +35 -0
  113. lisa_harness_sdk-0.1.0/src/sdk/guard/_backend.py +136 -0
  114. lisa_harness_sdk-0.1.0/src/sdk/guard/tests/__init__.py +0 -0
  115. lisa_harness_sdk-0.1.0/src/sdk/guard/tests/test_backend.py +114 -0
  116. lisa_harness_sdk-0.1.0/src/sdk/identity.py +48 -0
  117. lisa_harness_sdk-0.1.0/src/sdk/layout.py +35 -0
  118. lisa_harness_sdk-0.1.0/src/sdk/logging_setup.py +433 -0
  119. lisa_harness_sdk-0.1.0/src/sdk/mcp_host/__init__.py +43 -0
  120. lisa_harness_sdk-0.1.0/src/sdk/mcp_host/content_servers.py +223 -0
  121. lisa_harness_sdk-0.1.0/src/sdk/memory/__init__.py +5 -0
  122. lisa_harness_sdk-0.1.0/src/sdk/memory/backup.py +185 -0
  123. lisa_harness_sdk-0.1.0/src/sdk/memory/embeddings.py +50 -0
  124. lisa_harness_sdk-0.1.0/src/sdk/memory/store.py +785 -0
  125. lisa_harness_sdk-0.1.0/src/sdk/memory/tests/test_backup.py +143 -0
  126. lisa_harness_sdk-0.1.0/src/sdk/memory/tests/test_store.py +767 -0
  127. lisa_harness_sdk-0.1.0/src/sdk/pack_settings.py +61 -0
  128. lisa_harness_sdk-0.1.0/src/sdk/packs/__init__.py +13 -0
  129. lisa_harness_sdk-0.1.0/src/sdk/providers/__init__.py +77 -0
  130. lisa_harness_sdk-0.1.0/src/sdk/providers/anthropic_gateway.py +636 -0
  131. lisa_harness_sdk-0.1.0/src/sdk/providers/bedrock_gateway.py +376 -0
  132. lisa_harness_sdk-0.1.0/src/sdk/providers/google_gateway.py +574 -0
  133. lisa_harness_sdk-0.1.0/src/sdk/providers/openai_gateway.py +1765 -0
  134. lisa_harness_sdk-0.1.0/src/sdk/providers/token_counting.py +31 -0
  135. lisa_harness_sdk-0.1.0/src/sdk/py.typed +0 -0
  136. lisa_harness_sdk-0.1.0/src/sdk/router/README.md +682 -0
  137. lisa_harness_sdk-0.1.0/src/sdk/router/__init__.py +68 -0
  138. lisa_harness_sdk-0.1.0/src/sdk/router/default_config.py +34 -0
  139. lisa_harness_sdk-0.1.0/src/sdk/router/router.py +248 -0
  140. lisa_harness_sdk-0.1.0/src/sdk/storage/__init__.py +21 -0
  141. lisa_harness_sdk-0.1.0/src/sdk/storage/_backend.py +439 -0
  142. lisa_harness_sdk-0.1.0/src/sdk/storage/tests/__init__.py +0 -0
  143. lisa_harness_sdk-0.1.0/src/sdk/storage/tests/test_backend.py +343 -0
  144. lisa_harness_sdk-0.1.0/src/sdk/thread/__init__.py +48 -0
  145. lisa_harness_sdk-0.1.0/src/sdk/thread/_backend.py +755 -0
  146. lisa_harness_sdk-0.1.0/src/sdk/thread/_readout.py +587 -0
  147. lisa_harness_sdk-0.1.0/src/sdk/thread/_results.py +501 -0
  148. lisa_harness_sdk-0.1.0/src/sdk/thread/_search.py +171 -0
  149. lisa_harness_sdk-0.1.0/src/sdk/thread/_taskview.py +255 -0
  150. lisa_harness_sdk-0.1.0/src/sdk/thread/redaction.py +117 -0
  151. lisa_harness_sdk-0.1.0/src/sdk/workproducts.py +178 -0
  152. lisa_harness_sdk-0.1.0/src/sdk/workspace.py +112 -0
  153. lisa_harness_sdk-0.1.0/tests/conftest.py +46 -0
  154. lisa_harness_sdk-0.1.0/tests/test_anthropic_gateway.py +721 -0
  155. lisa_harness_sdk-0.1.0/tests/test_azure_gateway.py +505 -0
  156. lisa_harness_sdk-0.1.0/tests/test_bedrock_gateway.py +368 -0
  157. lisa_harness_sdk-0.1.0/tests/test_catalog_approval_continuation.py +51 -0
  158. lisa_harness_sdk-0.1.0/tests/test_catalog_module_path.py +140 -0
  159. lisa_harness_sdk-0.1.0/tests/test_catalog_timeout_zero.py +199 -0
  160. lisa_harness_sdk-0.1.0/tests/test_content_catalog_mode_provider_preference.py +192 -0
  161. lisa_harness_sdk-0.1.0/tests/test_copilot_customizations.py +539 -0
  162. lisa_harness_sdk-0.1.0/tests/test_default_available_capabilities.py +659 -0
  163. lisa_harness_sdk-0.1.0/tests/test_fetcher.py +415 -0
  164. lisa_harness_sdk-0.1.0/tests/test_gateway_streaming.py +402 -0
  165. lisa_harness_sdk-0.1.0/tests/test_google_gateway.py +480 -0
  166. lisa_harness_sdk-0.1.0/tests/test_import_boundaries.py +83 -0
  167. lisa_harness_sdk-0.1.0/tests/test_loader_strict.py +94 -0
  168. lisa_harness_sdk-0.1.0/tests/test_mcp_host_content_servers.py +252 -0
  169. lisa_harness_sdk-0.1.0/tests/test_memory_embeddings.py +104 -0
  170. lisa_harness_sdk-0.1.0/tests/test_merge_child_usage.py +150 -0
  171. lisa_harness_sdk-0.1.0/tests/test_openai_compatible_gateway.py +503 -0
  172. lisa_harness_sdk-0.1.0/tests/test_overlay_catalog.py +336 -0
  173. lisa_harness_sdk-0.1.0/tests/test_plan_mode_escalation.py +999 -0
  174. lisa_harness_sdk-0.1.0/tests/test_reflect_self.py +98 -0
  175. lisa_harness_sdk-0.1.0/tests/test_request_token_counting.py +53 -0
  176. lisa_harness_sdk-0.1.0/tests/test_router.py +710 -0
  177. lisa_harness_sdk-0.1.0/tests/test_self_model_telemetry.py +178 -0
  178. lisa_harness_sdk-0.1.0/tests/test_thread_archive_reads.py +485 -0
  179. lisa_harness_sdk-0.1.0/tests/test_thread_search.py +1843 -0
  180. lisa_harness_sdk-0.1.0/tests/test_tool_stream_output.py +81 -0
  181. 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
@@ -0,0 +1,3 @@
1
+ # Sub-turn budget-exhausted tail
2
+
3
+ BUDGET EXHAUSTED — call an action tool now or yield.
@@ -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.
@@ -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.
@@ -0,0 +1,4 @@
1
+ # No-verified-write warning footnote
2
+
3
+ ⚠️ This response produced no verified write;
4
+ available actions on the last turn were: {action_names}