Skip to content

Commit bb6877f

Browse files
committed
feat: add agent ID and token support for thread creation, enabling admin assignment
1 parent b29dc87 commit bb6877f

2 files changed

Lines changed: 65 additions & 1 deletion

File tree

src/main.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,8 @@ class ThreadCreate(BaseModel):
817817
metadata: dict | None = None
818818
system_prompt: str | None = None
819819
template: str | None = None # Template ID for defaults (UP-18)
820+
agent_id: str | None = None # Optional creator agent ID for admin assignment
821+
token: str | None = None # Optional creator token (required when agent_id is provided)
820822

821823

822824
class TemplateCreate(BaseModel):
@@ -968,12 +970,41 @@ async def api_create_thread(body: ThreadCreate):
968970

969971
try:
970972
db = await asyncio.wait_for(get_db(), timeout=DB_TIMEOUT)
973+
974+
creator_agent = None
975+
if body.agent_id:
976+
if not body.token:
977+
raise HTTPException(status_code=401, detail="token is required when agent_id is provided")
978+
token_valid = await asyncio.wait_for(
979+
crud.agent_verify_token(db, body.agent_id, body.token),
980+
timeout=DB_TIMEOUT,
981+
)
982+
if not token_valid:
983+
raise HTTPException(status_code=401, detail="Invalid agent_id/token")
984+
creator_agent = await asyncio.wait_for(crud.agent_get(db, body.agent_id), timeout=DB_TIMEOUT)
985+
if creator_agent is None:
986+
raise HTTPException(status_code=404, detail="Creator agent not found")
987+
971988
t = await asyncio.wait_for(
972989
crud.thread_create(db, body.topic, body.metadata, body.system_prompt, template=body.template),
973990
timeout=DB_TIMEOUT
974991
)
992+
993+
if creator_agent is not None:
994+
settings = await asyncio.wait_for(crud.thread_settings_get_or_create(db, t.id), timeout=DB_TIMEOUT)
995+
if settings.auto_administrator_enabled:
996+
creator_name = creator_agent.display_name or creator_agent.name
997+
await asyncio.wait_for(
998+
crud.thread_settings_set_creator_admin(db, t.id, creator_agent.id, creator_name),
999+
timeout=DB_TIMEOUT,
1000+
)
1001+
await asyncio.wait_for(
1002+
crud._set_agent_activity(db, creator_agent.id, "thread_create", touch_heartbeat=True),
1003+
timeout=DB_TIMEOUT,
1004+
)
1005+
9751006
sync = await asyncio.wait_for(
976-
crud.issue_reply_token(db, thread_id=t.id),
1007+
crud.issue_reply_token(db, thread_id=t.id, agent_id=creator_agent.id if creator_agent else None),
9771008
timeout=DB_TIMEOUT,
9781009
)
9791010
except asyncio.TimeoutError:

tests/test_admin_decision_api.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ def _register_agent(client: httpx.Client) -> str:
3838
return resp.json()["agent_id"]
3939

4040

41+
def _register_agent_with_token(client: httpx.Client) -> tuple[str, str]:
42+
resp = client.post(
43+
"/api/agents/register",
44+
json={"ide": "VS Code", "model": "GPT-5.3-Codex"},
45+
)
46+
assert resp.status_code == 200, resp.text
47+
payload = resp.json()
48+
return payload["agent_id"], payload["token"]
49+
50+
4151
def test_admin_decision_switch_then_keep():
4252
with _build_client() as client:
4353
_require_server_or_skip(client)
@@ -100,3 +110,26 @@ def test_admin_decision_switch_replaces_previous_admin():
100110
admin_resp = client.get(f"/api/threads/{thread_id}/admin")
101111
assert admin_resp.status_code == 200, admin_resp.text
102112
assert admin_resp.json()["admin_id"] == agent_b
113+
114+
115+
def test_thread_creator_agent_is_admin():
116+
with _build_client() as client:
117+
_require_server_or_skip(client)
118+
creator_id, creator_token = _register_agent_with_token(client)
119+
120+
create_resp = client.post(
121+
"/api/threads",
122+
json={
123+
"topic": f"creator-admin-{uuid.uuid4()}",
124+
"agent_id": creator_id,
125+
"token": creator_token,
126+
},
127+
)
128+
assert create_resp.status_code == 201, create_resp.text
129+
thread_id = create_resp.json()["id"]
130+
131+
admin_resp = client.get(f"/api/threads/{thread_id}/admin")
132+
assert admin_resp.status_code == 200, admin_resp.text
133+
payload = admin_resp.json()
134+
assert payload["admin_id"] == creator_id
135+
assert payload["admin_type"] == "creator"

0 commit comments

Comments
 (0)