Skip to content

Commit 1db00bc

Browse files
committed
feat: add events and fake sdk harness
1 parent ab89a55 commit 1db00bc

17 files changed

Lines changed: 1980 additions & 37 deletions

File tree

.planning/REQUIREMENTS.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ Which phases cover which requirements. Updated during roadmap creation.
150150
| Requirement | Phase | Status |
151151
|-------------|-------|--------|
152152
| PKG-01 | Phase 5 | Pending |
153-
| PKG-02 | Phase 2 | Pending |
153+
| PKG-02 | Phase 2 | Complete |
154154
| PKG-03 | Phase 1 | Complete |
155155
| PKG-04 | Phase 1 | Complete |
156156
| PKG-05 | Phase 5 | Pending |
@@ -161,20 +161,20 @@ Which phases cover which requirements. Updated during roadmap creation.
161161
| CORE-05 | Phase 1 | Complete |
162162
| CORE-06 | Phase 1 | Complete |
163163
| CORE-07 | Phase 1 | Complete |
164-
| EVNT-01 | Phase 2 | Pending |
165-
| EVNT-02 | Phase 2 | Pending |
166-
| EVNT-03 | Phase 2 | Pending |
167-
| EVNT-04 | Phase 2 | Pending |
168-
| EVNT-05 | Phase 2 | Pending |
164+
| EVNT-01 | Phase 2 | Complete |
165+
| EVNT-02 | Phase 2 | Complete |
166+
| EVNT-03 | Phase 2 | Complete |
167+
| EVNT-04 | Phase 2 | Complete |
168+
| EVNT-05 | Phase 2 | Complete |
169169
| ADPT-01 | Phase 3 | Pending |
170170
| ADPT-02 | Phase 3 | Pending |
171171
| ADPT-03 | Phase 3 | Pending |
172172
| ADPT-04 | Phase 3 | Pending |
173173
| ADPT-05 | Phase 4 | Pending |
174174
| ADPT-06 | Phase 4 | Pending |
175175
| ADPT-07 | Phase 4 | Pending |
176-
| TEST-01 | Phase 2 | Pending |
177-
| TEST-02 | Phase 2 | Pending |
176+
| TEST-01 | Phase 2 | Complete |
177+
| TEST-02 | Phase 2 | Complete |
178178
| TEST-03 | Phase 1 | Complete |
179179
| TEST-04 | Phase 1 | Complete |
180180
| TEST-05 | Phase 5 | Pending |

.planning/ROADMAP.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ packaging, and smoke-test guidance.
1818
Decimal phases appear between their surrounding integers in numeric order.
1919

2020
- [x] **Phase 1: Core Runtime Skeleton** - Installable core package with public task/result/runtime contracts.
21-
- [ ] **Phase 2: Events and Test Harness** - Optional extras skeleton, event system, and fake SDK contract tests.
21+
- [x] **Phase 2: Events and Test Harness** - Optional extras skeleton, event system, and fake SDK contract tests.
2222
- [ ] **Phase 3: Claude and Codex Runtimes** - First two real vendor adapters through the shared API.
2323
- [ ] **Phase 4: Antigravity and Cross-Runtime Proof** - Third adapter plus same-task multi-runtime proof and Mestre compatibility checks.
2424
- [ ] **Phase 5: Public Release Readiness** - Documentation, capability matrix, live-smoke guidance, and PyPI publish checklist.
@@ -118,7 +118,7 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5
118118
| Phase | Plans Complete | Status | Completed |
119119
|-------|----------------|--------|-----------|
120120
| 1. Core Runtime Skeleton | 3/3 | Complete | 2026-06-10 |
121-
| 2. Events and Test Harness | 0/3 | Not started | - |
121+
| 2. Events and Test Harness | 3/3 | Complete | 2026-06-10 |
122122
| 3. Claude and Codex Runtimes | 0/3 | Not started | - |
123123
| 4. Antigravity and Cross-Runtime Proof | 0/3 | Not started | - |
124124
| 5. Public Release Readiness | 0/3 | Not started | - |

.planning/STATE.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ gsd_state_version: '1.0'
33
status: executing
44
progress:
55
total_phases: 5
6-
completed_phases: 1
6+
completed_phases: 2
77
total_plans: 15
8-
completed_plans: 3
9-
percent: 20
8+
completed_plans: 6
9+
percent: 40
1010
---
1111

1212
# Project State
@@ -18,21 +18,21 @@ See: .planning/PROJECT.md (updated 2026-06-10)
1818
**Core value:** Developers can run the same agentic task through Claude, Codex,
1919
or Antigravity using one small, typed Python API while preserving the
2020
vendor-specific capabilities needed for real work.
21-
**Current focus:** Phase 2: Events and Test Harness
21+
**Current focus:** Phase 3: Claude and Codex Runtimes
2222

2323
## Current Position
2424

25-
Phase: 2 of 5 (Events and Test Harness)
25+
Phase: 3 of 5 (Claude and Codex Runtimes)
2626
Plan: 0 of 3 in current phase
27-
Status: Phase 1 complete; ready for Phase 2
28-
Last activity: 2026-06-10 - Phase 1 implemented the dependency-free package core.
27+
Status: Phase 2 complete; ready for Phase 3
28+
Last activity: 2026-06-10 - Phase 2 added normalized events and fake SDK contract helpers.
2929

30-
Progress: [##--------] 20%
30+
Progress: [####------] 40%
3131

3232
## Performance Metrics
3333

3434
**Velocity:**
35-
- Total plans completed: 3
35+
- Total plans completed: 6
3636
- Average duration: n/a
3737
- Total execution time: 0.0 hours
3838

@@ -41,9 +41,10 @@ Progress: [##--------] 20%
4141
| Phase | Plans | Total | Avg/Plan |
4242
|-------|-------|-------|----------|
4343
| 1. Core Runtime Skeleton | 3 | 3 | n/a |
44+
| 2. Events and Test Harness | 3 | 3 | n/a |
4445

4546
**Recent Trend:**
46-
- Last 5 plans: 01-01, 01-02, 01-03
47+
- Last 5 plans: 01-02, 01-03, 02-01, 02-02, 02-03
4748
- Trend: n/a
4849

4950
*Updated after each plan completion*
@@ -62,6 +63,8 @@ Recent decisions affecting current work:
6263
- Initialization: Extract runtime/adapters, not Mestre's full orchestration stack.
6364
- Phase 1: Core package stays dependency-free; vendor SDK imports are deferred
6465
to optional adapter modules.
66+
- Phase 2: Event payloads use a normalized dictionary shape and sanitize
67+
sensitive/high-volume attributes before emission.
6568

6669
### Pending Todos
6770

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Phase 2: Events and Test Harness - Context
2+
3+
**Gathered:** 2026-06-10
4+
**Status:** Ready for planning
5+
6+
<domain>
7+
## Phase Boundary
8+
9+
Add optional dependency extras, normalized runtime events, redaction/truncation
10+
defaults, and credential-free fake SDK fixtures for adapter contract testing.
11+
12+
</domain>
13+
14+
<decisions>
15+
## Implementation Decisions
16+
17+
### Events
18+
- Use the canonical event names from Mestre's vendor lane.
19+
- Emit dictionary events with `name`, `timestamp`, `summary`, and sanitized
20+
`attributes`.
21+
- Redact common credential keys and truncate large string values by default.
22+
- Treat event sinks as best-effort; sink failures must not abort runtimes.
23+
24+
### Test Harness
25+
- Keep fake SDK fixtures under `agent_runtime_kit.testing`.
26+
- Support scripted success, failure, missing dependency, unsupported input,
27+
timeout, session ids, structured output, and tool events.
28+
- Use the fake SDK runtime as the shared contract surface for later adapters.
29+
30+
### the agent's Discretion
31+
All implementation details are at the agent's discretion where they preserve
32+
dependency isolation and later adapter testability.
33+
34+
</decisions>
35+
36+
<code_context>
37+
## Existing Code Insights
38+
39+
### Reusable Assets
40+
- Phase 1 public models provide task/result/runtime primitives.
41+
42+
### Established Patterns
43+
- Core package modules avoid vendor SDK imports.
44+
45+
### Integration Points
46+
- `FakeAgentRuntime` now emits events through the same helpers real adapters
47+
will use.
48+
49+
</code_context>
50+
51+
<specifics>
52+
## Specific Ideas
53+
54+
Default tests must continue to pass without Claude, Codex, or Antigravity SDKs
55+
installed.
56+
57+
</specifics>
58+
59+
<deferred>
60+
## Deferred Ideas
61+
62+
Real adapter implementations remain deferred to phases 3 and 4.
63+
64+
</deferred>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Phase 2: Events and Test Harness - Plan
2+
3+
## Plan 02-01: Optional extras skeleton and dependency isolation tests
4+
5+
- Add `claude`, `codex`, `antigravity`, and `all` optional extras.
6+
- Add a test proving core import does not import vendor SDK modules.
7+
8+
## Plan 02-02: Event vocabulary, event sink, and redaction/truncation defaults
9+
10+
- Add normalized event helper functions for task, output, tool, and vendor-turn
11+
events.
12+
- Add safe best-effort emission.
13+
- Sanitize sensitive and high-volume payload values by default.
14+
15+
## Plan 02-03: Fake SDK harness and adapter contract test utilities
16+
17+
- Add `FakeSDKScenario`, `FakeSDKHarness`, `FakeSDKRuntime`, and
18+
`RecordingEventSink`.
19+
- Cover success, failure, timeout, structured output, session id, and tool
20+
event scenarios.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Phase 2: Events and Test Harness - Summary
2+
3+
Implemented event and testing infrastructure:
4+
5+
- Optional dependency extras for Claude, Codex, Antigravity, and all runtimes.
6+
- Normalized event helpers with redaction/truncation defaults.
7+
- Best-effort event emission through async sinks.
8+
- Fake SDK harness and runtime for adapter contract tests.
9+
- Tests for event ordering, sanitization, optional dependency isolation, fake
10+
SDK success/failure/timeout behavior, structured output, sessions, and tool
11+
events.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
status: passed
3+
---
4+
5+
# Phase 2 Verification
6+
7+
## Automated Checks
8+
9+
- `uv run pytest` - passed, 10 tests.
10+
- `uv run ruff check .` - passed.
11+
- `uv run mypy` - passed.
12+
13+
## Result
14+
15+
Phase 2 success criteria are satisfied. Default tests still run without vendor
16+
SDKs installed, normalized events cover task/output/tool/vendor-turn cases, and
17+
the fake SDK harness can script success, failure, timeout, structured output,
18+
session ids, and tool events.

pyproject.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ classifiers = [
2424
]
2525
dependencies = []
2626

27+
[project.optional-dependencies]
28+
claude = ["claude-agent-sdk>=0.2.87"]
29+
codex = ["openai-codex>=0.1.0b2"]
30+
antigravity = ["google-antigravity>=0.1.2"]
31+
all = [
32+
"claude-agent-sdk>=0.2.87",
33+
"google-antigravity>=0.1.2",
34+
"openai-codex>=0.1.0b2",
35+
]
36+
2737
[project.urls]
2838
Homepage = "https://github.com/ebarti/agent-sdk"
2939
Repository = "https://github.com/ebarti/agent-sdk"

src/agent_runtime_kit/__init__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@
2525
ToolCallAudit,
2626
Usage,
2727
)
28+
from agent_runtime_kit.events import (
29+
output_delta_event,
30+
safe_emit,
31+
task_completed_event,
32+
task_failed_event,
33+
task_started_event,
34+
tool_completed_event,
35+
tool_requested_event,
36+
vendor_turn_event,
37+
)
2838
from agent_runtime_kit.registry import RuntimeRegistry, create_default_registry
2939

3040
__all__ = [
@@ -51,4 +61,12 @@
5161
"UnsupportedTaskInputError",
5262
"Usage",
5363
"create_default_registry",
64+
"output_delta_event",
65+
"safe_emit",
66+
"task_completed_event",
67+
"task_failed_event",
68+
"task_started_event",
69+
"tool_completed_event",
70+
"tool_requested_event",
71+
"vendor_turn_event",
5472
]

src/agent_runtime_kit/_runtime.py

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@
1414
RuntimeAvailability,
1515
ToolCallAudit,
1616
)
17+
from agent_runtime_kit.events import (
18+
output_delta_event,
19+
safe_emit,
20+
task_completed_event,
21+
task_failed_event,
22+
task_started_event,
23+
tool_completed_event,
24+
tool_requested_event,
25+
vendor_turn_event,
26+
)
1727

1828

1929
class FakeAgentRuntime:
@@ -49,20 +59,52 @@ def availability(self) -> RuntimeAvailability:
4959
async def run(self, task: AgentTask) -> AgentResult:
5060
"""Return a deterministic result after validating capabilities."""
5161

52-
_ensure_supported(self.kind, self.capabilities, task)
53-
output = self._output if self._output is not None else f"Fake result for: {task.goal}"
54-
parsed = {"output": output} if task.output_schema is not None else None
55-
tool_calls = (
56-
ToolCallAudit(tool_name="fake", arguments={"goal": task.goal}, result_preview=output),
57-
)
58-
return AgentResult(
59-
output=output,
60-
parsed_output=parsed,
61-
tool_calls=tool_calls,
62-
session_id=task.session_id or task.task_id,
63-
rounds=1,
64-
metadata={"task_id": task.task_id, **self._metadata},
65-
)
62+
await safe_emit(task, task_started_event(task, self.kind))
63+
try:
64+
_ensure_supported(self.kind, self.capabilities, task)
65+
output = self._output if self._output is not None else f"Fake result for: {task.goal}"
66+
parsed = {"output": output} if task.output_schema is not None else None
67+
tool_call = ToolCallAudit(
68+
tool_name="fake",
69+
arguments={"goal": task.goal},
70+
result_preview=output,
71+
)
72+
await safe_emit(
73+
task,
74+
output_delta_event(task, self.kind, text=output),
75+
)
76+
await safe_emit(
77+
task,
78+
tool_requested_event(
79+
task,
80+
self.kind,
81+
tool_name="fake",
82+
arguments=tool_call.arguments,
83+
),
84+
)
85+
await safe_emit(task, tool_completed_event(task, self.kind, tool_call))
86+
await safe_emit(
87+
task,
88+
vendor_turn_event(
89+
task,
90+
self.kind,
91+
payload={"runtime": "fake", "round": 1},
92+
summary="fake runtime completed one turn",
93+
),
94+
)
95+
result = AgentResult(
96+
output=output,
97+
parsed_output=parsed,
98+
tool_calls=(tool_call,),
99+
session_id=task.session_id or task.task_id,
100+
rounds=1,
101+
metadata={"task_id": task.task_id, **self._metadata},
102+
)
103+
except Exception as exc:
104+
await safe_emit(task, task_failed_event(task, self.kind, error=str(exc)))
105+
raise
106+
await safe_emit(task, task_completed_event(task, self.kind, result))
107+
return result
66108

67109
async def cancel(self, task_id: str) -> None:
68110
"""Record cancellation requests for assertions."""

0 commit comments

Comments
 (0)