fix(tui): establish 2-tier readiness model, modal/hint token separation, and adapter modal contracts

- Separate TUI dialog detection into blocking gate layer (_MAM_MODAL_TOKENS) and recovery layer (_MAM_HINT_TOKENS) to eliminate idle dialog starvation
- Add modal_tokens contract across agent adapters (claude, cline) with dynamic scope facts resolution in send_keys_safe
- Implement fail-closed resolution for ambiguous same-kind panes (R-1) and enforce WORKSPACE_ROOT export (R-2)
- Preserve session on TUI readiness timeout when PID is alive for diagnostics
- Add comprehensive test suite in tests/test_c1_tui_readiness.py and adapter contract assertions in test_a4
This commit is contained in:
2026-08-28 15:21:10 +09:00
parent 4a5a093986
commit 17edf90676
9 changed files with 868 additions and 42 deletions
+3
View File
@@ -23,6 +23,9 @@ def main():
print(f"MAM_INPUT_PLACEHOLDER={q(adapter.input_placeholder or '')}")
print(f"MAM_INPUT_RULE_PATTERN={q(adapter.input_rule_pattern or '')}")
print(f"MAM_READY_TOKENS={q(adapter.ready_tokens)}")
print(f"MAM_STRONG_READY_TOKENS={q(adapter.strong_ready_tokens)}")
print(f"MAM_WEAK_READY_TOKENS={q(adapter.weak_ready_tokens)}")
print(f"MAM_MODAL_TOKENS={q(adapter.modal_tokens or '')}")
print(f"MAM_EXIT_KEY={q(adapter.exit_key)}")
print(f"MAM_DELEGATE_AGENT_KEY={q(adapter.delegate_agent_key)}")
@@ -40,6 +40,10 @@ class ClaudeAgentAdapter(BaseAgentAdapter):
def input_rule_pattern(self) -> str:
return '{10,}'
@property
def modal_tokens(self) -> Optional[str]:
return 'Try the new fullscreen renderer\\?'
def artifact_path(self, uuid: str, ctx: DiscoveryContext) -> str:
return f"{ctx.claude_dir}/{ctx.ws_key}/{uuid}.jsonl"
@@ -16,6 +16,18 @@ class ClineAgentAdapter(BaseAgentAdapter):
def ready_tokens(self) -> str:
return 'Cline|history|Chat|What can I do|slash commands'
@property
def strong_ready_tokens(self) -> str:
return 'Cline|What can I do|slash commands'
@property
def weak_ready_tokens(self) -> str:
return 'history|Chat'
@property
def modal_tokens(self) -> Optional[str]:
return 'Select API Provider|Enter API Key|Select an API provider|API Provider|Cline API key'
@property
def exit_key(self) -> str:
return '/exit'
+12
View File
@@ -73,6 +73,18 @@ class BaseAgentAdapter:
def input_rule_pattern(self) -> Optional[str]:
return None
@property
def modal_tokens(self) -> Optional[str]:
return None
@property
def strong_ready_tokens(self) -> str:
return self.ready_tokens
@property
def weak_ready_tokens(self) -> str:
return ''
def derive_session_name(self, slug: str, role: str = "creator") -> str:
r = role.lower()
return f"{slug}-{r}-{self.name}"