Live demo: https://kyn-xi.vercel.app/
Built for the Nozomio Hackathon — Track 4: The Company Brain (Nia + Hyperspell).
A context-engineering system for complex work problems. The supervisor decides what context to gather, calls capability subagents (Code / Company / Memory / Action), and ships real artifacts. The system gets smarter every run.
"Kyn" is a placeholder name. Swappable later.
Idea + research + build guidelines live in .claude/.
CLI:
.venv/bin/python scripts/run_agent.py "your problem here"HTTP API + Frontend (live UI on the same origin):
.venv/bin/uvicorn server.main:app --reload --port 8000
# open http://localhost:8000 in a browserThe static HTML mockup at KynUIRunView.html is now wired to the live backend. Three modes via URL:
| URL | What it does |
|---|---|
http://localhost:8000/ |
Redirects to the run view. Type a problem in the box, click Run. |
http://localhost:8000/static/KynUIRunView.html?problem=Why+are+payments+failing |
Auto-runs that problem on load. |
http://localhost:8000/static/KynUIRunView.html?run_id=<uuid> |
Replay mode — loads a past run from disk and animates events back. Add &instant=1 to skip the pacing. |
The page POSTs to /agent/run and parses the SSE stream client-side, mutating the existing DOM hooks (.chat-body, .trail, .answer-body, .evidence, .mini-log, KPIs). Markdown rendered via the marked CDN script. No build step. All JS lives in a single <script> block at the bottom of KynUIRunView.html.
| Method | Path | Purpose |
|---|---|---|
GET |
/healthz |
Health check |
POST |
/agent/run |
SSE stream — events as the agent works |
POST |
/agent/run/sync |
JSON — wait for full result |
GET |
/runs |
List past run summaries |
GET |
/runs/{problem_id} |
Replay a past run (full payload) |
GET |
/artifacts/{problem_id} |
List artifact filenames for a run |
GET |
/artifacts/{problem_id}/{filename} |
Serve a markdown artifact |
GET |
/docs |
Auto-generated OpenAPI schema (browse it) |
Pydantic schemas live in server/main.py. TypeScript can be codegened from /openapi.json.
type RunEvent =
| { type: "started"; problem_id: string; problem: string; started_at: string; ts: string }
| { type: "agent_updated"; agent: string; ts: string }
| { type: "tool_called"; tool: string; arguments: string; ts: string }
| { type: "tool_output"; output: string; ts: string }
| { type: "message"; text: string; ts: string }
| { type: "handoff_requested"; ts: string }
| { type: "handoff_occurred"; target: string; ts: string }
| { type: "info"; message: string; ts: string }
| { type: "warning"; message: string; ts: string }
| { type: "error"; error: string; ts: string }
| { type: "final"; data: RunFinal; ts: string }
| { type: "done"; ts: string };Sample frontend code:
// EventSource (read-only): no POST body — use fetch streaming instead for POST.
const resp = await fetch("http://localhost:8000/agent/run", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ problem: "..." }),
});
const reader = resp.body!.getReader();
const decoder = new TextDecoder();
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// each SSE message is "event: <type>\ndata: <json>\n\n"
for (const block of buffer.split("\n\n")) {
const eventMatch = block.match(/event: (.+)/);
const dataMatch = block.match(/data: (.+)/);
if (eventMatch && dataMatch) {
const event = JSON.parse(dataMatch[1]);
// dispatch on event.type ...
}
}
}Frontend can also develop against any runs/run-*.json file directly — same shape as the final event's data.