tests: pin ink engine in _make_tui_argv npm-bootstrap tests (post-merge semantic fix)

Main's rewritten test_tui_npm_install.py tests call _make_tui_argv expecting
the Ink/npm flow unconditionally; with the dual-engine dispatch merged in,
_resolve_tui_engine() auto-selects opentui whenever ui-opentui/dist is built
in the repo, routing the call away from the path under test (first subprocess
became 'node --version' instead of 'npm run build'). Pin the engine to ink
via an autouse fixture, mirroring the existing pinning precedent in
test_tui_resume_flow.py.
This commit is contained in:
alt-glitch 2026-06-12 10:32:40 +05:30
parent ab37440ce6
commit e1067dbbe5
756 changed files with 79874 additions and 19585 deletions

View file

@ -606,6 +606,63 @@ class MemoryStore:
raise RuntimeError(f"Failed to write memory file {path}: {e}")
def _apply_write_gate(action: str, target: str, content: Optional[str],
old_text: Optional[str]) -> Optional[str]:
"""Evaluate the memory write gate. Returns a JSON tool-result string when
the write should NOT proceed normally (blocked or staged), or None when the
caller should perform the real write.
Only the mutating actions (add/replace/remove) are gated.
"""
if action not in {"add", "replace", "remove"}:
return None
try:
from tools import write_approval as wa
except Exception:
# If the gate module can't load, fail open (current behaviour) rather
# than blocking all memory writes.
return None
# Build a small inline summary/detail for the foreground approval prompt.
label = "user profile" if target == "user" else "memory"
if action == "add":
summary = f"add to {label}"
detail = content or ""
elif action == "replace":
summary = f"replace in {label}"
detail = f"old: {old_text}\nnew: {content}"
else: # remove
summary = f"remove from {label}"
detail = old_text or ""
decision = wa.evaluate_gate(wa.MEMORY, inline_summary=summary, inline_detail=detail)
if decision.allow:
return None
if decision.blocked:
return tool_error(decision.message, success=False)
# stage
payload = {
"action": action,
"target": target,
"content": content,
"old_text": old_text,
}
record = wa.stage_write(
wa.MEMORY, payload,
summary=f"{summary}: {detail[:120]}",
origin=wa.current_origin(),
)
return json.dumps(
{"success": True, "staged": True, "pending_id": record["id"],
"message": decision.message},
ensure_ascii=False,
)
def memory_tool(
action: str,
target: str = "memory",
@ -624,21 +681,29 @@ def memory_tool(
if target not in {"memory", "user"}:
return tool_error(f"Invalid target '{target}'. Use 'memory' or 'user'.", success=False)
# Validate required params BEFORE the gate so an invalid write is rejected
# immediately instead of being staged and only failing at approve time.
if action == "add" and not content:
return tool_error("Content is required for 'add' action.", success=False)
if action == "replace" and (not old_text or not content):
missing = "old_text" if not old_text else "content"
return tool_error(f"{missing} is required for 'replace' action.", success=False)
if action == "remove" and not old_text:
return tool_error("old_text is required for 'remove' action.", success=False)
# Approval gate: when on, stages the write (background/gateway) or prompts
# inline (interactive CLI); when off (default) passes straight through.
gate_result = _apply_write_gate(action, target, content, old_text)
if gate_result is not None:
return gate_result
if action == "add":
if not content:
return tool_error("Content is required for 'add' action.", success=False)
result = store.add(target, content)
elif action == "replace":
if not old_text:
return tool_error("old_text is required for 'replace' action.", success=False)
if not content:
return tool_error("content is required for 'replace' action.", success=False)
result = store.replace(target, old_text, content)
elif action == "remove":
if not old_text:
return tool_error("old_text is required for 'remove' action.", success=False)
result = store.remove(target, old_text)
else:
@ -652,7 +717,23 @@ def check_memory_requirements() -> bool:
return True
# =============================================================================
def apply_memory_pending(payload: Dict[str, Any], store: "MemoryStore") -> Dict[str, Any]:
"""Replay a staged memory write directly against the store, bypassing the
write gate. Called by the /memory approve handler.
Returns the store's result dict.
"""
action = payload.get("action")
target = payload.get("target", "memory")
content = payload.get("content") or ""
old_text = payload.get("old_text") or ""
if action == "add":
return store.add(target, content)
if action == "replace":
return store.replace(target, old_text, content)
if action == "remove":
return store.remove(target, old_text)
return {"success": False, "error": f"Unknown staged action '{action}'."}
# OpenAI Function-Calling Schema
# =============================================================================