diff --git a/turnstone/console/collector.py b/turnstone/console/collector.py
index 010b8e26..09b18c57 100644
--- a/turnstone/console/collector.py
+++ b/turnstone/console/collector.py
@@ -394,7 +394,8 @@ class ClusterCollector:
{
"type": "ws_created",
"ws_id": ws_id,
- "name": ws.get("name", ""),
+ "name": ws.get("title", "") or ws.get("name", ""),
+ "title": ws.get("title", ""),
"node_id": node_id,
}
)
@@ -418,8 +419,8 @@ class ClusterCollector:
"content": new_w.get("content", ""),
}
)
- old_name = old_ws.get("name", "")
- new_name = new_w.get("name", "")
+ old_name = old_ws.get("title", "") or old_ws.get("name", "")
+ new_name = new_w.get("title", "") or new_w.get("name", "")
if old_name != new_name and new_name:
pending.append({"type": "ws_rename", "ws_id": ws_id, "name": new_name})
node.workstreams = new_ws
diff --git a/turnstone/console/server.py b/turnstone/console/server.py
index d40c8629..a37b324c 100644
--- a/turnstone/console/server.py
+++ b/turnstone/console/server.py
@@ -129,21 +129,36 @@ _JS_PROXY_SHIM = """\
"""
_CONSOLE_BANNER_TEMPLATE = (
- '
'
- '
"
- "← Console"
- '
\u2502'
- '
NODE_ID_PLACEHOLDER'
+ '
"
)
-# Injected "
+# Injected "
+)
_VALID_NODE_ID = re.compile(r"^[a-zA-Z0-9._-]+$")
@@ -446,6 +461,7 @@ async def create_workstream(request: Request) -> JSONResponse:
raw_node_id = body.get("node_id", "")
raw_name = body.get("name", "")
raw_model = body.get("model", "")
+ raw_judge_model = body.get("judge_model", "")
raw_initial_message = body.get("initial_message", "")
raw_skill = body.get("skill", "")
raw_resume_ws = body.get("resume_ws", "")
@@ -455,6 +471,8 @@ async def create_workstream(request: Request) -> JSONResponse:
raw_name = "" if raw_name is None else None
if not isinstance(raw_model, str):
raw_model = "" if raw_model is None else None
+ if not isinstance(raw_judge_model, str):
+ raw_judge_model = "" if raw_judge_model is None else None
if not isinstance(raw_initial_message, str):
raw_initial_message = "" if raw_initial_message is None else None
if not isinstance(raw_skill, str):
@@ -465,19 +483,21 @@ async def create_workstream(request: Request) -> JSONResponse:
raw_node_id is None
or raw_name is None
or raw_model is None
+ or raw_judge_model is None
or raw_initial_message is None
or raw_skill is None
or raw_resume_ws is None
):
return JSONResponse(
{
- "error": "node_id, name, model, initial_message, skill, and resume_ws must be strings"
+ "error": "node_id, name, model, judge_model, initial_message, skill, and resume_ws must be strings"
},
status_code=400,
)
node_id = raw_node_id
name = raw_name[:256]
model = raw_model[:128]
+ judge_model = raw_judge_model[:128]
initial_message = raw_initial_message[:4096]
skill = raw_skill[:256]
resume_ws = raw_resume_ws[:64]
@@ -509,6 +529,7 @@ async def create_workstream(request: Request) -> JSONResponse:
ws_body = {
"name": name,
"model": model,
+ "judge_model": judge_model,
"initial_message": initial_message,
"skill": skill,
"resume_ws": resume_ws,
@@ -918,7 +939,7 @@ async def proxy_index(request: Request) -> Response:
page = page.replace('href="/shared/', f'href="{prefix}/shared/')
page = page.replace('src="/shared/', f'src="{prefix}/shared/')
# Inject console-return banner + proxy shim after
- banner = _CONSOLE_BANNER_TEMPLATE.replace("NODE_ID_PLACEHOLDER", html.escape(node_id))
+ banner = _CONSOLE_BANNER_TEMPLATE.replace("NODE_ID_PLACEHOLDER", html.escape(node_id)).replace("NODE_LINK_PLACEHOLDER", html.escape(prefix + "/"))
shim = (
"