symphony-harness 0.0.1__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 (38) hide show
  1. symphony_harness-0.0.1/LICENSE +21 -0
  2. symphony_harness-0.0.1/PKG-INFO +57 -0
  3. symphony_harness-0.0.1/README.md +41 -0
  4. symphony_harness-0.0.1/pyproject.toml +34 -0
  5. symphony_harness-0.0.1/pyproject.toml.orig +35 -0
  6. symphony_harness-0.0.1/src/core_harness/__init__.py +88 -0
  7. symphony_harness-0.0.1/src/core_harness/addons/__init__.py +45 -0
  8. symphony_harness-0.0.1/src/core_harness/addons/addon.py +67 -0
  9. symphony_harness-0.0.1/src/core_harness/addons/compaction/__init__.py +76 -0
  10. symphony_harness-0.0.1/src/core_harness/addons/compaction/policy.py +335 -0
  11. symphony_harness-0.0.1/src/core_harness/addons/persistence/__init__.py +104 -0
  12. symphony_harness-0.0.1/src/core_harness/addons/subagent/__init__.py +304 -0
  13. symphony_harness-0.0.1/src/core_harness/addons/subagent/background.py +66 -0
  14. symphony_harness-0.0.1/src/core_harness/config.py +123 -0
  15. symphony_harness-0.0.1/src/core_harness/context/__init__.py +39 -0
  16. symphony_harness-0.0.1/src/core_harness/context/compact.py +210 -0
  17. symphony_harness-0.0.1/src/core_harness/context/state.py +316 -0
  18. symphony_harness-0.0.1/src/core_harness/errors.py +15 -0
  19. symphony_harness-0.0.1/src/core_harness/events.py +200 -0
  20. symphony_harness-0.0.1/src/core_harness/extensions/__init__.py +39 -0
  21. symphony_harness-0.0.1/src/core_harness/extensions/api.py +36 -0
  22. symphony_harness-0.0.1/src/core_harness/extensions/dispatch.py +51 -0
  23. symphony_harness-0.0.1/src/core_harness/extensions/events.py +20 -0
  24. symphony_harness-0.0.1/src/core_harness/extensions/loader.py +107 -0
  25. symphony_harness-0.0.1/src/core_harness/extensions/manifest.py +72 -0
  26. symphony_harness-0.0.1/src/core_harness/extensions/recorder.py +47 -0
  27. symphony_harness-0.0.1/src/core_harness/extensions/registry.py +66 -0
  28. symphony_harness-0.0.1/src/core_harness/harness.py +443 -0
  29. symphony_harness-0.0.1/src/core_harness/loop/__init__.py +9 -0
  30. symphony_harness-0.0.1/src/core_harness/loop/session.py +412 -0
  31. symphony_harness-0.0.1/src/core_harness/loop/stream.py +261 -0
  32. symphony_harness-0.0.1/src/core_harness/loop/tools.py +286 -0
  33. symphony_harness-0.0.1/src/core_harness/models.py +172 -0
  34. symphony_harness-0.0.1/src/core_harness/ops.py +40 -0
  35. symphony_harness-0.0.1/src/core_harness/py.typed +0 -0
  36. symphony_harness-0.0.1/src/core_harness/timing.py +127 -0
  37. symphony_harness-0.0.1/src/core_harness/tools.py +93 -0
  38. symphony_harness-0.0.1/src/core_harness/turn_runner.py +280 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sarthak Chauhan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,57 @@
1
+ Metadata-Version: 2.4
2
+ Name: symphony-harness
3
+ Version: 0.0.1
4
+ Summary: Symphony agent harness: provider-agnostic tool loop, control-plane events, run limits, and compaction (import as core_harness)
5
+ Author: Sarthak Chauhan
6
+ Author-email: Sarthak Chauhan <sarthakchauhann@gmail.com>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Requires-Dist: symphony-core
10
+ Requires-Dist: pydantic>=2.0.0
11
+ Requires-Python: >=3.11
12
+ Project-URL: Homepage, https://github.com/Sarthakischauhan/symphony
13
+ Project-URL: Documentation, https://github.com/Sarthakischauhan/symphony/blob/main/docs/packages/symphony-harness.md
14
+ Project-URL: Source, https://github.com/Sarthakischauhan/symphony/tree/main/core_harness
15
+ Description-Content-Type: text/markdown
16
+
17
+ # symphony-harness
18
+
19
+ A product-agnostic async agent loop for Python: model turns, callable tools, operational limits, and a typed event stream. Import it as `core_harness`.
20
+
21
+ **Availability:** this package is not currently published on PyPI. Use it through the repository's uv workspace; start with the [source installation guide](../docs/getting-started/installation.md).
22
+
23
+ ## Minimal example
24
+
25
+ ```python
26
+ import asyncio
27
+ from core_ai import build_default_registry, default_model_id
28
+ from core_harness import CoreHarness, Tool
29
+
30
+ def read_file(path: str) -> str:
31
+ """Read a UTF-8 file."""
32
+ with open(path, encoding="utf-8") as file:
33
+ return file.read()
34
+
35
+ async def main() -> None:
36
+ harness = CoreHarness(
37
+ registry=build_default_registry(),
38
+ registry=build_default_registry(),
39
+ model_id=default_model_id(registry),
40
+ system_prompt="Answer concisely and use tools when useful.",
41
+ tools=[Tool(read_file)],
42
+ )
43
+ result = await harness.run("Summarize README.md", session_id="example")
44
+ print(result.output_text)
45
+
46
+ asyncio.run(main())
47
+ ```
48
+
49
+ `CoreHarness` does not provide a filesystem boundary or default approval policy: attach only tools you intend to expose, and add product-level policy where needed. See [SECURITY.md](../SECURITY.md).
50
+
51
+ ## Build with the harness
52
+
53
+ - [Harness guide](../docs/packages/symphony-harness.md): tools, runs, subagents, limits, and context.
54
+ - [Extend the harness](../docs/developer-guide/extending.md): add-on hooks and child behavior.
55
+ - [Event overview](../docs/developer-guide/events.md) and [detailed event catalog](docs/events.md).
56
+ - [Background child lifecycle](docs/background-children.md).
57
+ - [Architecture](../docs/developer-guide/architecture.md).
@@ -0,0 +1,41 @@
1
+ # symphony-harness
2
+
3
+ A product-agnostic async agent loop for Python: model turns, callable tools, operational limits, and a typed event stream. Import it as `core_harness`.
4
+
5
+ **Availability:** this package is not currently published on PyPI. Use it through the repository's uv workspace; start with the [source installation guide](../docs/getting-started/installation.md).
6
+
7
+ ## Minimal example
8
+
9
+ ```python
10
+ import asyncio
11
+ from core_ai import build_default_registry, default_model_id
12
+ from core_harness import CoreHarness, Tool
13
+
14
+ def read_file(path: str) -> str:
15
+ """Read a UTF-8 file."""
16
+ with open(path, encoding="utf-8") as file:
17
+ return file.read()
18
+
19
+ async def main() -> None:
20
+ harness = CoreHarness(
21
+ registry=build_default_registry(),
22
+ registry=build_default_registry(),
23
+ model_id=default_model_id(registry),
24
+ system_prompt="Answer concisely and use tools when useful.",
25
+ tools=[Tool(read_file)],
26
+ )
27
+ result = await harness.run("Summarize README.md", session_id="example")
28
+ print(result.output_text)
29
+
30
+ asyncio.run(main())
31
+ ```
32
+
33
+ `CoreHarness` does not provide a filesystem boundary or default approval policy: attach only tools you intend to expose, and add product-level policy where needed. See [SECURITY.md](../SECURITY.md).
34
+
35
+ ## Build with the harness
36
+
37
+ - [Harness guide](../docs/packages/symphony-harness.md): tools, runs, subagents, limits, and context.
38
+ - [Extend the harness](../docs/developer-guide/extending.md): add-on hooks and child behavior.
39
+ - [Event overview](../docs/developer-guide/events.md) and [detailed event catalog](docs/events.md).
40
+ - [Background child lifecycle](docs/background-children.md).
41
+ - [Architecture](../docs/developer-guide/architecture.md).
@@ -0,0 +1,34 @@
1
+ [project]
2
+ name = "symphony-harness"
3
+ version = "0.0.1"
4
+ description = "Symphony agent harness: provider-agnostic tool loop, control-plane events, run limits, and compaction (import as core_harness)"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ requires-python = ">=3.11"
9
+ dependencies = [
10
+ "symphony-core",
11
+ "pydantic>=2.0.0",
12
+ ]
13
+
14
+ [[project.authors]]
15
+ name = "Sarthak Chauhan"
16
+ email = "sarthakchauhann@gmail.com"
17
+
18
+ [project.urls]
19
+ Homepage = "https://github.com/Sarthakischauhan/symphony"
20
+ Documentation = "https://github.com/Sarthakischauhan/symphony/blob/main/docs/packages/symphony-harness.md"
21
+ Source = "https://github.com/Sarthakischauhan/symphony/tree/main/core_harness"
22
+
23
+ [tool.uv.sources.symphony-core]
24
+ workspace = true
25
+
26
+ [tool.uv.build-backend]
27
+ module-name = "core_harness"
28
+
29
+ [dependency-groups]
30
+ dev = ["pytest>=9.1.1"]
31
+
32
+ [build-system]
33
+ requires = ["uv_build>=0.9.8,<0.10.0"]
34
+ build-backend = "uv_build"
@@ -0,0 +1,35 @@
1
+ [project]
2
+ name = "symphony-harness"
3
+ version = "0.0.1"
4
+ description = "Symphony agent harness: provider-agnostic tool loop, control-plane events, run limits, and compaction (import as core_harness)"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ authors = [
9
+ { name = "Sarthak Chauhan", email = "sarthakchauhann@gmail.com" }
10
+ ]
11
+ requires-python = ">=3.11"
12
+ dependencies = [
13
+ "symphony-core",
14
+ "pydantic>=2.0.0",
15
+ ]
16
+
17
+ [project.urls]
18
+ Homepage = "https://github.com/Sarthakischauhan/symphony"
19
+ Documentation = "https://github.com/Sarthakischauhan/symphony/blob/main/docs/packages/symphony-harness.md"
20
+ Source = "https://github.com/Sarthakischauhan/symphony/tree/main/core_harness"
21
+
22
+ [tool.uv.sources]
23
+ symphony-core = { workspace = true }
24
+
25
+ [dependency-groups]
26
+ dev = [
27
+ "pytest>=9.1.1",
28
+ ]
29
+
30
+ [build-system]
31
+ requires = ["uv_build>=0.9.8,<0.10.0"]
32
+ build-backend = "uv_build"
33
+
34
+ [tool.uv.build-backend]
35
+ module-name = "core_harness"
@@ -0,0 +1,88 @@
1
+ from core_harness.addons import (
2
+ Addon,
3
+ ChildConfig,
4
+ ChildIdentity,
5
+ CompactionAddon,
6
+ Compactor,
7
+ KeepDropPlan,
8
+ KeepSystemRecentCompactor,
9
+ PersistenceAddon,
10
+ SubagentAddon,
11
+ compaction_from_config,
12
+ plan_keep_drop,
13
+ )
14
+ from core_harness.addons.persistence import (
15
+ Checkpoint,
16
+ NullPersistence,
17
+ Persistence,
18
+ )
19
+ from core_harness.context import (
20
+ ContextReport,
21
+ bound_tool_result,
22
+ build_context_report,
23
+ )
24
+ from core_harness.config import (
25
+ HarnessConfig,
26
+ load_harness_config,
27
+ resolve_harness_config,
28
+ )
29
+ from core_harness.events import EventLog, EventSink, HumanInteraction, InteractionPort
30
+ from core_harness.ops import Answer, Interrupt, Op, UserInput
31
+ from core_harness.errors import HarnessCancelled, HarnessLimitExceeded
32
+ from core_harness.harness import CoreHarness
33
+ from core_harness.models import (
34
+ ControlPlaneEvent,
35
+ ControlPlaneEventType,
36
+ EVENT_SCHEMA_VERSION,
37
+ HarnessResult,
38
+ PendingToolCall,
39
+ RunLimits,
40
+ ToolCall,
41
+ ToolResult,
42
+ UsageTotals,
43
+ )
44
+ from core_harness.tools import Tool
45
+
46
+ __all__ = [
47
+ "Answer",
48
+ "Addon",
49
+ "Checkpoint",
50
+ "ChildConfig",
51
+ "ChildIdentity",
52
+ "CompactionAddon",
53
+ "Compactor",
54
+ "ContextReport",
55
+ "ControlPlaneEvent",
56
+ "ControlPlaneEventType",
57
+ "CoreHarness",
58
+ "EVENT_SCHEMA_VERSION",
59
+ "EventLog",
60
+ "EventSink",
61
+ "HumanInteraction",
62
+ "InteractionPort",
63
+ "Interrupt",
64
+ "Op",
65
+ "UserInput",
66
+ "HarnessCancelled",
67
+ "HarnessLimitExceeded",
68
+ "HarnessResult",
69
+ "HarnessConfig",
70
+ "KeepDropPlan",
71
+ "KeepSystemRecentCompactor",
72
+ "NullPersistence",
73
+ "PendingToolCall",
74
+ "Persistence",
75
+ "PersistenceAddon",
76
+ "RunLimits",
77
+ "SubagentAddon",
78
+ "Tool",
79
+ "ToolCall",
80
+ "ToolResult",
81
+ "UsageTotals",
82
+ "bound_tool_result",
83
+ "build_context_report",
84
+ "compaction_from_config",
85
+ "load_harness_config",
86
+ "resolve_harness_config",
87
+ "plan_keep_drop",
88
+ ]
@@ -0,0 +1,45 @@
1
+ """Opt-in harness add-ons.
2
+
3
+ ``CoreHarness`` is an extension surface. Pass ``addons=[...]`` at construction
4
+ or call ``register_addon``. There is no directory discovery and no skill
5
+ loader; skills can hang off this same attach path later.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from core_harness.addons.addon import Addon
11
+ from core_harness.addons.compaction import (
12
+ CompactionAddon,
13
+ Compactor,
14
+ KeepDropPlan,
15
+ KeepSystemRecentCompactor,
16
+ compaction_from_config,
17
+ plan_keep_drop,
18
+ )
19
+ from core_harness.addons.persistence import (
20
+ Checkpoint,
21
+ CheckpointStatus,
22
+ NullPersistence,
23
+ Persistence,
24
+ PersistenceAddon,
25
+ )
26
+ from core_harness.addons.subagent import ChildConfig, ChildIdentity, SubagentAddon
27
+
28
+
29
+ __all__ = [
30
+ "Addon",
31
+ "Checkpoint",
32
+ "CheckpointStatus",
33
+ "ChildConfig",
34
+ "ChildIdentity",
35
+ "CompactionAddon",
36
+ "Compactor",
37
+ "KeepDropPlan",
38
+ "KeepSystemRecentCompactor",
39
+ "NullPersistence",
40
+ "Persistence",
41
+ "PersistenceAddon",
42
+ "SubagentAddon",
43
+ "compaction_from_config",
44
+ "plan_keep_drop",
45
+ ]
@@ -0,0 +1,67 @@
1
+ """Attachable harness extensions.
2
+
3
+ Subclass :class:`Addon` and override hooks to opt in.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import Any, Optional
9
+
10
+
11
+ class Addon:
12
+ """Harness extension. Hooks are no-ops until overridden.
13
+
14
+ Public seams (override to opt in): ``before_run``, ``before_turn``,
15
+ ``before_tool``, ``after_turn``, ``after_run``, ``on_tool``,
16
+ ``on_compact``. ``before_tool`` may return a deny reason; the others
17
+ observe. Inherit on spawn by implementing :meth:`fork_for_child`.
18
+ """
19
+
20
+ name: str = ""
21
+
22
+ def attach(self, harness: Any) -> None:
23
+ """Mount this add-on onto a harness. Called once from ``register_addon``."""
24
+ del harness
25
+ return None
26
+
27
+ def fork_for_child(self, parent_harness: Any) -> Optional[Addon]:
28
+ """Child inherit policy: return an instance to inherit, or ``None`` to skip.
29
+
30
+ The default skips, so children do not receive this instance.
31
+ ``ChildConfig.addons`` / ``addon_factory`` replace this path when set.
32
+ """
33
+ del parent_harness
34
+ return None
35
+
36
+ async def before_run(self, **payload: Any) -> None:
37
+ del payload
38
+ return None
39
+
40
+ async def before_turn(self, **payload: Any) -> None:
41
+ del payload
42
+ return None
43
+
44
+ async def before_tool(self, **payload: Any) -> Optional[str]:
45
+ """Return a deny reason to skip the tool, or ``None`` to allow it."""
46
+ del payload
47
+ return None
48
+
49
+ async def after_turn(self, **payload: Any) -> None:
50
+ del payload
51
+ return None
52
+
53
+ async def after_run(self, **payload: Any) -> None:
54
+ """Called after a successful run, before ``CoreHarness.run`` returns."""
55
+ del payload
56
+ return None
57
+
58
+ async def on_tool(self, **payload: Any) -> None:
59
+ del payload
60
+ return None
61
+
62
+ async def on_compact(self, **payload: Any) -> None:
63
+ del payload
64
+ return None
65
+
66
+
67
+ __all__ = ["Addon"]
@@ -0,0 +1,76 @@
1
+ """Compaction add-on: mount a ``Compactor`` onto the harness."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Optional
6
+
7
+ from core_harness.addons.addon import Addon
8
+ from core_harness.addons.compaction.policy import (
9
+ COMPACTION_CONTINUATION,
10
+ Compactor,
11
+ KeepDropPlan,
12
+ KeepSystemRecentCompactor,
13
+ compaction_header,
14
+ compaction_target,
15
+ dropped_turn_facts,
16
+ plan_keep_drop,
17
+ summarize_dropped_turns,
18
+ )
19
+ from core_harness.config import HarnessConfig
20
+
21
+
22
+ class CompactionAddon(Addon):
23
+ """Mount a ``Compactor`` onto ``CoreHarness.state``."""
24
+
25
+ name = "compaction"
26
+
27
+ def __init__(
28
+ self,
29
+ compactor: Compactor | None = None,
30
+ *,
31
+ keep_recent_tools: int = 32,
32
+ target_tokens: Optional[int] = None,
33
+ ) -> None:
34
+ self.compactor = compactor or KeepSystemRecentCompactor(
35
+ keep_recent_tools=keep_recent_tools,
36
+ target_tokens=target_tokens,
37
+ )
38
+ if isinstance(self.compactor, KeepSystemRecentCompactor):
39
+ keep_recent_tools = self.compactor.keep_recent_tools
40
+ target_tokens = self.compactor.target_tokens
41
+ self.keep_recent_tools = keep_recent_tools
42
+ self.target_tokens = target_tokens
43
+
44
+ def attach(self, harness: Any) -> None:
45
+ harness.state.compactor = self.compactor
46
+
47
+ def fork_for_child(self, parent_harness: Any) -> CompactionAddon:
48
+ """New add-on with a fresh compactor using the same settings."""
49
+ del parent_harness
50
+ return CompactionAddon(
51
+ keep_recent_tools=self.keep_recent_tools,
52
+ target_tokens=self.target_tokens,
53
+ )
54
+
55
+
56
+ def compaction_from_config(config: HarnessConfig) -> CompactionAddon:
57
+ """Build the keep-system-recent add-on from harness settings."""
58
+ return CompactionAddon(
59
+ keep_recent_tools=config.compaction_keep_recent_tools,
60
+ target_tokens=config.context_target_tokens,
61
+ )
62
+
63
+
64
+ __all__ = [
65
+ "COMPACTION_CONTINUATION",
66
+ "CompactionAddon",
67
+ "Compactor",
68
+ "KeepDropPlan",
69
+ "KeepSystemRecentCompactor",
70
+ "compaction_from_config",
71
+ "compaction_header",
72
+ "compaction_target",
73
+ "dropped_turn_facts",
74
+ "plan_keep_drop",
75
+ "summarize_dropped_turns",
76
+ ]