Files
simbarag/blueprints/scheduled_messages/validation.py
T
ryan 5809ad3adb
CI / backend (pull_request) Successful in 42s
CI / frontend (pull_request) Failing after 7s
CI / backend (push) Successful in 41s
CI / frontend (push) Failing after 7s
Add durable chat runs, shared agent runtime, reliable jobs, history tools and citations
Shared runtime
- One agent entry point (runtime.invoke_agent_for_user / prepare_agent_turn) for
  web, email, iMessage, WhatsApp, scheduled and cron, so identity, permissions,
  memories, summaries and metrics match across channels.
- Serialize turns per checkpoint thread; /query validates input and returns 409
  while a durable run owns the conversation.

Reconnectable chat (durable runs)
- POST /runs creates an idempotent, owner-scoped run; GET .../events replays an
  ordered SSE log by cursor, so generation survives refresh and device switches.
  Stop cancels; disconnect only detaches. /stream-query is a compatibility adapter.
- Coalesce streamed deltas (150 ms) and wake same-process subscribers instead of
  polling; keep buffered text when a run is stopped.
- Drain runs on shutdown; serve via Hypercorn with a shutdown timeout covering
  the drain (and no longer in debug mode). CHAT_RUN_* settings are forwarded.

Scheduled messages and cron
- Atomic delivery claims, attempt history and explicit retries; ambiguous sends
  become "uncertain" and are never resent automatically.
- Cron triggers need a run_id/Idempotency-Key; legacy jobs need an explicit owner.
- Cancelling never rewrites a started delivery; series stop via tombstones.
  Legacy sent rows are not forked into new series. Malformed bodies return 400.

History, integrations and retrieval
- Message-content search, pinned conversations, JSON/Markdown export and a
  memory editor.
- Integration status panel and YNAB data-freshness provenance.
- Date-filtered document/note search with stable citations and a source viewer.

Migration 20 is generated by aerich.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:11:04 -04:00

26 lines
944 B
Python

"""Request-body checks shared by the scheduled message and cron endpoints."""
from quart import jsonify, request
async def json_object(text_fields: tuple[str, ...], nullable: tuple[str, ...] = ()):
"""Return (data, None) for a non-empty object whose text fields are strings.
Fields in `nullable` may also be an explicit null. Otherwise return
(None, error_response) so malformed bodies get a 400 instead of failing on
`.get`/`.strip` with a 500.
"""
data = await request.get_json(silent=True)
if not isinstance(data, dict) or not data:
return None, (jsonify({"error": "Invalid payload"}), 400)
wrong = [
name
for name in text_fields
if name in data
and not isinstance(data[name], str)
and not (data[name] is None and name in nullable)
]
if wrong:
return None, (jsonify({"error": f"{', '.join(wrong)} must be text"}), 400)
return data, None