A toolbar toggle, a hub setting off by default, shows a speech bubble per agent anchored at its face in every view and at its projected node in 3D; an agent without an anchor speaks from a caption bar. The speaking agent's bubble is the largest and reveals its line word by word over the audio duration, or at 180 words a minute without TTS, a failed TTS request included; queued agents wait as smaller ellipsis bubbles sized by order, and a finished bubble fades out while the next one grows. Comic Neue 400 latin is vendored under SIL OFL 1.1 and loaded through the FontFace API, so the CSP stays unchanged. Residuals: the caption says speaking while bubbles run silently; the word reveal follows the wall clock, not the audio clock; the e2e suite runs once in the unify gate (operator order 2026-11-11 21:15), score gate exit 1 accepted under that order. 🎯 Fix: 82 % 💥 Break: 5 % 💸 Cost: 100 % ⏱️ Time: 100 % 🛡️ Security: 100 % 🏆 Score: 78 🚦 Gate: ⛔ fix 82 % < 90 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
288 lines
14 KiB
Python
288 lines
14 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
import secrets
|
|
from collections.abc import Callable
|
|
from http import HTTPStatus
|
|
from urllib.parse import urlsplit
|
|
|
|
from .engines import STT_LIMIT
|
|
from .exchange import PLAIN_CSP, Exchange
|
|
|
|
BODY_LIMIT = 1 << 20
|
|
UPLOAD_LIMIT = 50 << 20
|
|
PAGE_CSP = "sandbox allow-scripts"
|
|
SETTINGS_ROUTE = "/api/settings"
|
|
TTS_ROUTE = "/api/tts"
|
|
WAV_TYPE = "audio/wav"
|
|
STT_ROUTE = "/api/stt"
|
|
ROUTE = re.compile(r"/reviews/([0-9a-f]{16})/(page|files|approve|reject)")
|
|
ITEM = re.compile(r"/api/(asks|decisions)/([0-9a-f]{16})(?:/(allow|deny))?")
|
|
ENTRY = re.compile(r"/api/stream/([0-9]{1,12})")
|
|
BOARD = re.compile(r"/api/board/([a-z-]{1,32})")
|
|
UPLOADS = "/api/uploads"
|
|
POSTS = ("/api/asks", UPLOADS, "/api/messages")
|
|
STORED = {
|
|
"page": (".html", "text/html; charset=utf-8", PAGE_CSP),
|
|
"files": (".json", "application/json", PLAIN_CSP),
|
|
}
|
|
DECISIONS = {"approve": "approved", "reject": "rejected"}
|
|
INBOX_ROUTE = re.compile(r"/api/inbox/(approve|mark|dismiss)")
|
|
UNDO_ROUTE = re.compile(r"/api/inbox/undo/([0-9a-f]{16})")
|
|
RULE_ROUTE = re.compile(r"/api/rules/([0-9a-f]{16})/(enable|disable|delete)")
|
|
REQUEST_ROUTE = re.compile(r"/api/rule-requests/([0-9a-f]{16})/(sign|accept)")
|
|
RUNNER_ROUTE = re.compile(r"/api/runner/([0-9a-f]{16})/(claim|report)")
|
|
PROOF_ROUTE = re.compile(r"/api/reviews/([0-9a-f]{16})")
|
|
SIGN_ROUTE = "/api/sign"
|
|
RULE_SIGN_ROUTE = "/api/rules/sign"
|
|
CLEANUP_ROUTE = "/api/cleanup"
|
|
CLEANUP_STATE = re.compile(r"/api/cleanup/([0-9a-f]{16})")
|
|
OFFICES_ROUTE = "/api/offices"
|
|
OFFICE_STATE = re.compile(r"/api/offices/([0-9a-f]{16})")
|
|
TASKS_ROUTE = "/api/tasks"
|
|
VENDOR_ROUTE = re.compile(r"/vendor/([a-z0-9.-]{1,64})")
|
|
SCRIPT_TYPE = "text/javascript; charset=utf-8"
|
|
FONT_TYPE = "font/woff2"
|
|
VENDOR = {
|
|
"3d-force-graph.min.js": ("3d-force-graph/3d-force-graph.min.js", SCRIPT_TYPE),
|
|
"three.module.js": ("three/three.module.js", SCRIPT_TYPE),
|
|
"three.core.js": ("three/three.core.js", SCRIPT_TYPE),
|
|
"comic-neue.woff2": ("comic-neue/comic-neue-latin-400-normal.woff2", FONT_TYPE),
|
|
}
|
|
VOICE_ROUTE = "/api/voice"
|
|
VOICE_ITEM = re.compile(r"/api/voice/([0-9a-f]{16})(\.wav)?")
|
|
MESSAGE_SIGN_ROUTE = "/api/messages/sign"
|
|
BOARD_SIGN_ROUTE = "/api/board/sign"
|
|
SETTINGS_SIGN_ROUTE = "/api/settings/sign"
|
|
CHOICES_ROUTE = "/api/choices"
|
|
CHOICES_SIGN_ROUTE = "/api/choices/sign"
|
|
WAV_POSTS = (STT_ROUTE, VOICE_ROUTE)
|
|
ACTION_POSTS = ("/api/rules", "/api/nl", SETTINGS_ROUTE, TTS_ROUTE, STT_ROUTE, SIGN_ROUTE, RULE_SIGN_ROUTE, CLEANUP_ROUTE, OFFICES_ROUTE, TASKS_ROUTE, VOICE_ROUTE, MESSAGE_SIGN_ROUTE, SETTINGS_SIGN_ROUTE, CHOICES_ROUTE, CHOICES_SIGN_ROUTE)
|
|
|
|
|
|
class Handler(Exchange):
|
|
def speak(self, body: bytes) -> None:
|
|
headers = {"Content-Security-Policy": PLAIN_CSP}
|
|
self.attempt(lambda: self.server.hub.speech.synthesize(body), lambda wav: self.send(HTTPStatus.OK, WAV_TYPE, wav, headers))
|
|
|
|
def hear(self, body: bytes) -> None:
|
|
self.answer(lambda: self.server.hub.speech.transcribe(body), HTTPStatus.OK)
|
|
|
|
def replay_voice(self, note_id: str) -> None:
|
|
headers = {"Content-Security-Policy": PLAIN_CSP}
|
|
self.attempt(lambda: self.server.hub.voice_notes.audio(note_id), lambda wav: self.send(HTTPStatus.OK, WAV_TYPE, wav, headers))
|
|
|
|
def do_GET(self) -> None:
|
|
path = urlsplit(self.path).path
|
|
hub = self.server.hub
|
|
route = ROUTE.fullmatch(path)
|
|
item = ITEM.fullmatch(path)
|
|
entry = ENTRY.fullmatch(path)
|
|
proof = PROOF_ROUTE.fullmatch(path)
|
|
vendored = VENDOR_ROUTE.fullmatch(path)
|
|
cleanup = CLEANUP_STATE.fullmatch(path)
|
|
office = OFFICE_STATE.fullmatch(path)
|
|
voice = VOICE_ITEM.fullmatch(path)
|
|
if self.host() is None:
|
|
self.send_text(HTTPStatus.FORBIDDEN, "forbidden host")
|
|
elif path == "/":
|
|
self.index()
|
|
elif vendored and vendored.group(1) in VENDOR:
|
|
self.vendored(*VENDOR[vendored.group(1)])
|
|
elif voice and voice.group(2):
|
|
self.replay_voice(voice.group(1))
|
|
elif voice:
|
|
self.answer(lambda: hub.voice_notes.get(voice.group(1)), HTTPStatus.OK)
|
|
elif cleanup:
|
|
self.answer(lambda: hub.cleanup_state(cleanup.group(1)), HTTPStatus.OK)
|
|
elif office:
|
|
self.answer(lambda: hub.office_state(office.group(1)), HTTPStatus.OK)
|
|
elif proof:
|
|
self.answer(lambda: hub.review_proof(proof.group(1)), HTTPStatus.OK)
|
|
elif path == "/api/state":
|
|
self.answer(lambda: hub.window(self.query()), HTTPStatus.OK)
|
|
elif path == "/api/stream":
|
|
self.answer(lambda: hub.stream(self.query()), HTTPStatus.OK)
|
|
elif path == "/api/events":
|
|
self.answer(lambda: hub.events(self.query()), HTTPStatus.OK)
|
|
elif path == "/api/tree":
|
|
self.answer(hub.repositories, HTTPStatus.OK)
|
|
elif path == "/api/subagents":
|
|
self.answer(lambda: hub.subagents(self.query()), HTTPStatus.OK)
|
|
elif path == "/api/runner/jobs":
|
|
if self.client():
|
|
self.answer(hub.runner_jobs, HTTPStatus.OK)
|
|
else:
|
|
self.send_text(HTTPStatus.FORBIDDEN, "forbidden: the runner calls with the X-Kvb-Orchestrator header and no origin only")
|
|
elif entry:
|
|
self.answer(lambda: hub.entry(int(entry.group(1))), HTTPStatus.OK)
|
|
elif item and item.group(1) == "asks" and item.group(3) is None:
|
|
self.answer(lambda: hub.ask_wait(item.group(2), self.query()), HTTPStatus.OK)
|
|
elif route and route.group(2) in STORED:
|
|
self.stored(route.group(1), route.group(2))
|
|
elif route or item or path == "/mcp" or path in POSTS or path in ACTION_POSTS or BOARD.fullmatch(path) or self.posted(path):
|
|
self.not_allowed()
|
|
else:
|
|
self.send_text(HTTPStatus.NOT_FOUND, "not found")
|
|
|
|
def action(self, path: str) -> Callable[[bytes], None] | None:
|
|
hub = self.server.hub
|
|
route = ROUTE.fullmatch(path)
|
|
item = ITEM.fullmatch(path)
|
|
board = BOARD.fullmatch(path)
|
|
if route and route.group(2) in DECISIONS:
|
|
return lambda body: self.answer(lambda: hub.verdict(route.group(1), DECISIONS[route.group(2)], body), HTTPStatus.OK)
|
|
if item and item.group(3):
|
|
return lambda body: self.answer(lambda: hub.settle(item.group(1), item.group(2), item.group(3), body), HTTPStatus.OK)
|
|
if path == UPLOADS:
|
|
return lambda body: self.answer(lambda: hub.upload(self.query(), body), HTTPStatus.CREATED)
|
|
if path == "/api/messages":
|
|
return lambda body: self.answer(lambda: hub.message(body), HTTPStatus.CREATED)
|
|
if path == BOARD_SIGN_ROUTE:
|
|
return lambda body: self.answer(lambda: hub.sign_board(body), HTTPStatus.OK)
|
|
if board:
|
|
return lambda body: self.answer(lambda: hub.board(board.group(1), body), HTTPStatus.OK)
|
|
return self.inbox_action(path)
|
|
|
|
def inbox_action(self, path: str) -> Callable[[bytes], None] | None:
|
|
hub = self.server.hub
|
|
inbox = INBOX_ROUTE.fullmatch(path)
|
|
undo = UNDO_ROUTE.fullmatch(path)
|
|
rule = RULE_ROUTE.fullmatch(path)
|
|
request = REQUEST_ROUTE.fullmatch(path)
|
|
if inbox and inbox.group(1) == "approve":
|
|
return lambda body: self.answer(lambda: hub.approve(body), HTTPStatus.ACCEPTED)
|
|
if inbox and inbox.group(1) == "dismiss":
|
|
return lambda body: self.answer(lambda: hub.dismiss(body), HTTPStatus.OK)
|
|
if request and request.group(2) == "sign":
|
|
return lambda _body: self.answer(lambda: hub.sign_request(request.group(1)), HTTPStatus.OK)
|
|
if request:
|
|
return lambda body: self.answer(lambda: hub.accept_request(request.group(1), body), HTTPStatus.CREATED)
|
|
if path == SIGN_ROUTE:
|
|
return lambda body: self.answer(lambda: hub.sign(body), HTTPStatus.OK)
|
|
if path == RULE_SIGN_ROUTE:
|
|
return lambda body: self.answer(lambda: hub.sign_rule(body), HTTPStatus.OK)
|
|
if path == CLEANUP_ROUTE:
|
|
return lambda body: self.answer(lambda: hub.cleanup(body), HTTPStatus.ACCEPTED)
|
|
if path == OFFICES_ROUTE:
|
|
return lambda body: self.answer(lambda: hub.appoint(body), HTTPStatus.ACCEPTED)
|
|
if path == TASKS_ROUTE:
|
|
return lambda body: self.answer(lambda: hub.change_tasks(body), HTTPStatus.ACCEPTED)
|
|
if inbox:
|
|
return lambda body: self.answer(lambda: hub.mark(body), HTTPStatus.OK)
|
|
if undo:
|
|
return lambda _body: self.answer(lambda: hub.undo(undo.group(1)), HTTPStatus.OK)
|
|
if path == "/api/rules":
|
|
return lambda body: self.answer(lambda: hub.add_rule(body), HTTPStatus.CREATED)
|
|
if rule:
|
|
return lambda _body: self.answer(lambda: hub.rule_action(rule.group(1), rule.group(2)), HTTPStatus.OK)
|
|
if path == "/api/nl":
|
|
return lambda body: self.answer(lambda: hub.nl(body), HTTPStatus.OK)
|
|
if path == SETTINGS_ROUTE:
|
|
return lambda body: self.answer(lambda: hub.save_settings(body), HTTPStatus.OK)
|
|
if path == SETTINGS_SIGN_ROUTE:
|
|
return lambda body: self.answer(lambda: hub.sign_settings(body), HTTPStatus.OK)
|
|
if path == TTS_ROUTE:
|
|
return self.speak
|
|
if path == STT_ROUTE:
|
|
return self.hear
|
|
if path == VOICE_ROUTE:
|
|
return lambda body: self.answer(lambda: hub.voice_notes.submit(body), HTTPStatus.ACCEPTED)
|
|
if path == MESSAGE_SIGN_ROUTE:
|
|
return lambda body: self.answer(lambda: hub.sign_message(body), HTTPStatus.OK)
|
|
if path == CHOICES_SIGN_ROUTE:
|
|
return lambda body: self.answer(lambda: hub.sign_choices(body), HTTPStatus.OK)
|
|
if path == CHOICES_ROUTE:
|
|
return lambda body: self.answer(lambda: hub.request_choices(body), HTTPStatus.CREATED)
|
|
return None
|
|
|
|
def posted(self, path: str) -> bool:
|
|
return any(pattern.fullmatch(path) for pattern in (INBOX_ROUTE, UNDO_ROUTE, RULE_ROUTE, REQUEST_ROUTE, RUNNER_ROUTE))
|
|
|
|
def runner(self, path: str, body: bytes) -> None:
|
|
hub = self.server.hub
|
|
route = RUNNER_ROUTE.fullmatch(path)
|
|
if route.group(2) == "claim":
|
|
self.answer(lambda: hub.claim(route.group(1)), HTTPStatus.OK)
|
|
else:
|
|
self.answer(lambda: hub.report(route.group(1), body), HTTPStatus.OK)
|
|
|
|
def receiver(self, path: str, host: str) -> Callable[[bytes], None] | None:
|
|
"""Returns:
|
|
What handles the body once every header guard let the request
|
|
pass, or None after a guard answered it.
|
|
"""
|
|
if path == "/mcp":
|
|
return self.mcp_receiver(host)
|
|
if path == "/api/asks":
|
|
if self.local(host):
|
|
return lambda body: self.answer(lambda: self.server.hub.ask(body), HTTPStatus.CREATED)
|
|
self.send_text(HTTPStatus.FORBIDDEN, "forbidden: local POST with the X-Kvb-Orchestrator header and no foreign origin only")
|
|
return None
|
|
if RUNNER_ROUTE.fullmatch(path):
|
|
if self.client():
|
|
return lambda body: self.runner(path, body)
|
|
self.send_text(HTTPStatus.FORBIDDEN, "forbidden: the runner calls with the X-Kvb-Orchestrator header and no origin only")
|
|
return None
|
|
action = self.action(path)
|
|
if action is None:
|
|
self.send_text(HTTPStatus.NOT_FOUND, "not found")
|
|
elif not self.guarded(host):
|
|
self.send_text(HTTPStatus.FORBIDDEN, "forbidden: same-origin POST with the X-Kvb-Orchestrator header only")
|
|
elif path in WAV_POSTS and self.headers.get_content_type() != WAV_TYPE:
|
|
self.send_text(HTTPStatus.UNSUPPORTED_MEDIA_TYPE, f"use {WAV_TYPE}")
|
|
else:
|
|
return action
|
|
return None
|
|
|
|
def do_POST(self) -> None:
|
|
path = urlsplit(self.path).path
|
|
length = self.body_length({UPLOADS: UPLOAD_LIMIT, STT_ROUTE: STT_LIMIT, VOICE_ROUTE: STT_LIMIT}.get(path, BODY_LIMIT))
|
|
if length is None:
|
|
return
|
|
host = self.host()
|
|
if host is None:
|
|
self.send_text(HTTPStatus.FORBIDDEN, "forbidden host")
|
|
return
|
|
receive = self.receiver(path, host)
|
|
if receive is not None:
|
|
receive(self.rfile.read(length))
|
|
|
|
def index(self) -> None:
|
|
nonce = secrets.token_urlsafe(18)
|
|
page, csp = self.server.hub.render(nonce)
|
|
headers = {"Content-Security-Policy": csp, "X-Frame-Options": "DENY"}
|
|
self.send(HTTPStatus.OK, "text/html; charset=utf-8", page.encode("utf-8"), headers)
|
|
|
|
def vendored(self, name: str, content_type: str) -> None:
|
|
headers = {"Content-Security-Policy": PLAIN_CSP, "Cache-Control": "private, max-age=86400"}
|
|
self.attempt((self.server.hub.vendor / name).read_bytes, lambda data: self.send(HTTPStatus.OK, content_type, data, headers))
|
|
|
|
def stored(self, review_id: str, name: str) -> None:
|
|
suffix, content_type, csp = STORED[name]
|
|
reviews = self.server.hub.reviews
|
|
path = reviews.stored(review_id, suffix)
|
|
if reviews.get(review_id) is None or not path.is_file():
|
|
self.send_text(HTTPStatus.NOT_FOUND, "not found")
|
|
return
|
|
self.attempt(path.read_bytes, lambda data: self.send(HTTPStatus.OK, content_type, data, {"Content-Security-Policy": csp}))
|
|
|
|
def mcp_receiver(self, host: str) -> Callable[[bytes], None] | None:
|
|
origins = self.headers.get_all("Origin")
|
|
if origins is not None and origins != [f"http://{host}"]:
|
|
self.send_text(HTTPStatus.FORBIDDEN, "foreign origin")
|
|
elif self.headers.get_content_type() != "application/json":
|
|
self.send_text(HTTPStatus.UNSUPPORTED_MEDIA_TYPE, "use application/json")
|
|
else:
|
|
return self.mcp
|
|
return None
|
|
|
|
def mcp(self, body: bytes) -> None:
|
|
status, reply = self.server.hub.rpc(body)
|
|
if reply is not None:
|
|
self.send_json(status, reply)
|
|
return
|
|
self.send_response(status)
|
|
self.send_header("Content-Length", "0")
|
|
self.end_headers()
|