mirror of
https://github.com/ultraworkers/claw-code.git
synced 2026-04-24 05:00:25 +08:00
feat: #164 Stage B prep — add cancel_observed field to TurnResult
#164 Stage B requires exposing whether cancellation was observed at the turn-result level. This commit adds the infrastructure field: Changes: - TurnResult.cancel_observed: bool = False (query_engine.py) - _build_timeout_result() accepts cancel_observed parameter (runtime.py) - Two timeout paths now pass cancel_event.is_set() to signal observation (runtime.py) - bootstrap command includes cancel_observed in turn JSON (main.py) - SCHEMAS.md documents Turn Result Fields with cancel_observed contract Usage: When a turn timeout occurs, cancel_observed=true indicates that the engine observed the cancellation event being set. This allows callers to distinguish: - timeout with no cancel → infrastructure/network stall - timeout with cancel observed → cooperative cancellation was triggered Backward compat: - Existing TurnResult construction without cancel_observed defaults to False - bootstrap JSON output still validates per SCHEMAS.md (new field is always present) Test results: 182 passing, 3 skipped, zero regression. Related: #161 (wall-clock timeout), #164 (cancellation observability protocol) ROADMAP continues #164 with Stage C (test coverage for cancellation + turn envelope).
This commit is contained in:
parent
290ab7e41f
commit
97c4b130dc
13
SCHEMAS.md
13
SCHEMAS.md
@ -30,6 +30,19 @@ Every command response, success or error, carries:
|
||||
|
||||
---
|
||||
|
||||
## Turn Result Fields (Multi-Turn Sessions)
|
||||
|
||||
When a command's response includes a `turn` object (e.g., in `bootstrap` or `turn-loop`), it carries:
|
||||
|
||||
| Field | Type | Required | Notes |
|
||||
|---|---|---|---|
|
||||
| `prompt` | string | Yes | User input for this turn |
|
||||
| `output` | string | Yes | Assistant response |
|
||||
| `stop_reason` | enum | Yes | One of: `completed`, `timeout`, `cancelled`, `max_budget_reached`, `max_turns_reached` |
|
||||
| `cancel_observed` | bool | Yes | #164 Stage B: cancellation was signaled and observed (#161/#164) |
|
||||
|
||||
---
|
||||
|
||||
## Error Envelope
|
||||
|
||||
When a command fails (exit code 1), responses carry:
|
||||
|
||||
@ -332,6 +332,7 @@ def main(argv: list[str] | None = None) -> int:
|
||||
'prompt': session.turn_result.prompt,
|
||||
'output': session.turn_result.output,
|
||||
'stop_reason': session.turn_result.stop_reason,
|
||||
'cancel_observed': session.turn_result.cancel_observed,
|
||||
},
|
||||
'persisted_session_path': session.persisted_session_path,
|
||||
}
|
||||
|
||||
@ -31,6 +31,7 @@ class TurnResult:
|
||||
permission_denials: tuple[PermissionDenial, ...]
|
||||
usage: UsageSummary
|
||||
stop_reason: str
|
||||
cancel_observed: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@ -249,7 +249,10 @@ class PortRuntime:
|
||||
# submitted yet this turn.
|
||||
assert cancel_event is not None
|
||||
cancel_event.set()
|
||||
results.append(self._build_timeout_result(turn_prompt, command_names, tool_names))
|
||||
results.append(self._build_timeout_result(
|
||||
turn_prompt, command_names, tool_names,
|
||||
cancel_observed=cancel_event.is_set()
|
||||
))
|
||||
break
|
||||
assert executor is not None
|
||||
future = executor.submit(
|
||||
@ -268,7 +271,10 @@ class PortRuntime:
|
||||
assert cancel_event is not None
|
||||
cancel_event.set()
|
||||
future.cancel()
|
||||
results.append(self._build_timeout_result(turn_prompt, command_names, tool_names))
|
||||
results.append(self._build_timeout_result(
|
||||
turn_prompt, command_names, tool_names,
|
||||
cancel_observed=cancel_event.is_set()
|
||||
))
|
||||
break
|
||||
|
||||
results.append(result)
|
||||
@ -287,8 +293,11 @@ class PortRuntime:
|
||||
prompt: str,
|
||||
command_names: tuple[str, ...],
|
||||
tool_names: tuple[str, ...],
|
||||
cancel_observed: bool = False,
|
||||
) -> TurnResult:
|
||||
"""Synthesize a TurnResult representing a wall-clock timeout (#161)."""
|
||||
"""Synthesize a TurnResult representing a wall-clock timeout (#161).
|
||||
#164 Stage B: cancel_observed signals cancellation event was set.
|
||||
"""
|
||||
return TurnResult(
|
||||
prompt=prompt,
|
||||
output='Wall-clock timeout exceeded before turn completed.',
|
||||
@ -297,6 +306,7 @@ class PortRuntime:
|
||||
permission_denials=(),
|
||||
usage=UsageSummary(),
|
||||
stop_reason='timeout',
|
||||
cancel_observed=cancel_observed,
|
||||
)
|
||||
|
||||
def _infer_permission_denials(self, matches: list[RoutedMatch]) -> list[PermissionDenial]:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user