Fix status-cache poisoning, add HTTP timeouts, coerce custom status names

- _status_choices no longer caches failures: a transient /ticket_form_fields
  error used to cache [] forever, silently degrading unresolved=True to an
  unfiltered listing (including Resolved/Closed) until container restart.
  unresolved=True and list_ticket_statuses now return an explicit error when
  the status list can't be fetched instead of degrading silently.
- All API calls (server and skill helper) now use a 30s timeout with a clean
  error, so a hung connection can't block a tool call indefinitely.
- _coerce_status falls back to the instance's live status list, so custom
  names like "Working" coerce to their id instead of 400ing at the API.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-08 10:38:56 -06:00
parent 0b6747812f
commit e38841918d
2 changed files with 43 additions and 16 deletions

View File

@@ -93,7 +93,7 @@ def request(method: str, path: str, params: dict[str, str], data: str | None) ->
req = urllib.request.Request(url, data=body, method=method.upper(), headers=headers)
try:
with urllib.request.urlopen(req) as resp:
with urllib.request.urlopen(req, timeout=30) as resp:
raw = resp.read().decode()
out = {"ok": True, "status": resp.status,
"data": json.loads(raw) if raw else {}}
@@ -116,6 +116,8 @@ def request(method: str, path: str, params: dict[str, str], data: str | None) ->
"method": method.upper(), "url": url}
except urllib.error.URLError as e:
return {"ok": False, "error": f"Network error: {e.reason}", "url": url}
except TimeoutError:
return {"ok": False, "error": "Request timed out after 30s", "url": url}
def main() -> None: