chore: add agents/reports to gitignore and update agent infrastructure with timestamp streaming, write budget, and codename generation

- Add `agents/reports/` to `.gitignore` to prevent generated agent report files from being tracked
- Implement `_TimestampStream` class and `install_timestamps()` function in `agents/agent.py` for prefixing stdout/stderr with timestamps and elapsed time
- Export `install_timestamps`, `set_write_budget`, `clear_write_budget`, and `set_shell_restricted` from `agents/base.py`; add `_RUN_LOCK`, `AGENT_ICONS` dictionary, and `WRITE_BUDGET = 20` constant
- Add `report_codename()` function and `CODENAME_ADJECTIVES`/`CODENAME_ANIMALS` tuples to `agents/core/__init__.py` for generating random agent report codenames
- Expand `WRITE_TOOLS` tuple in `agents/core/__init__.py` to include `replace_lines`, `insert_lines`, and `delete_lines`; remove `SWARM_TOOLS` from `payloads_for` exclusion list
- Update `CLAUDE.md` and `AGENTS.md` documentation with dataset column initialization rules and new agent infrastructure details
- Reorder route table in `README.md` to list `/uploads` before `/messages` and document Swagger/ReDoc/OpenAPI schema endpoints
This commit is contained in:
2026-06-12 03:37:12 +00:00
parent 7fc9b0f715
commit 31841fece4
80 changed files with 2095 additions and 323 deletions
+6 -2
View File
@@ -50,7 +50,9 @@ def test_generate_avatar_svg_different_for_different_seeds():
def test_generate_avatar_svg_fallback_on_exception(monkeypatch):
import multiavatar.multiavatar as ma
import importlib
ma = importlib.import_module("multiavatar.multiavatar")
def _boom(*args, **kwargs):
raise RuntimeError("avatar backend down")
@@ -63,7 +65,9 @@ def test_generate_avatar_svg_fallback_on_exception(monkeypatch):
def test_generate_avatar_svg_includes_rect_for_fallback(monkeypatch):
import multiavatar.multiavatar as ma
import importlib
ma = importlib.import_module("multiavatar.multiavatar")
monkeypatch.setattr(ma, "multiavatar", lambda *a, **k: None)
svg = generate_avatar_svg("zeta")
+180
View File
@@ -0,0 +1,180 @@
import time
import requests
from tests.conftest import BASE_URL
from devplacepy.database import get_table
AJAX = {"X-Requested-With": "fetch"}
_counter = [0]
def _session():
_counter[0] += 1
name = f"cmt{int(time.time() * 1000)}{_counter[0]}"
s = requests.Session()
s.post(
f"{BASE_URL}/auth/signup",
data={
"username": name,
"email": f"{name}@t.dev",
"password": "secret123",
"confirm_password": "secret123",
},
allow_redirects=True,
)
return s, name
def _create_post(session, title):
r = session.post(
f"{BASE_URL}/posts/create",
data={
"content": "Post for comment test.",
"title": title,
"topic": "devlog",
},
allow_redirects=False,
)
slug = r.headers["location"].split("/posts/")[-1]
return get_table("posts").find_one(slug=slug)["uid"]
def test_create_comment_on_post(app_server):
s, _ = _session()
title = f"comment-test-{int(time.time() * 1000)}"
post_uid = _create_post(s, title)
r = s.post(
f"{BASE_URL}/comments/create",
data={
"content": "Nice post!",
"target_type": "post",
"post_uid": post_uid,
"target_uid": post_uid,
},
allow_redirects=False,
)
assert r.status_code in (302, 303)
comment = get_table("comments").find_one(target_uid=post_uid)
assert comment is not None
assert comment["content"] == "Nice post!"
def test_create_reply_to_comment(app_server):
s, _ = _session()
title = f"reply-test-{int(time.time() * 1000)}"
post_uid = _create_post(s, title)
s.post(
f"{BASE_URL}/comments/create",
data={
"content": "Parent comment",
"target_type": "post",
"post_uid": post_uid,
"target_uid": post_uid,
},
allow_redirects=False,
)
parent = get_table("comments").find_one(target_uid=post_uid)
r = s.post(
f"{BASE_URL}/comments/create",
data={
"content": "Reply to parent",
"target_type": "post",
"post_uid": post_uid,
"target_uid": post_uid,
"parent_uid": parent["uid"],
},
allow_redirects=False,
)
assert r.status_code in (302, 303)
replies = list(get_table("comments").find(parent_uid=parent["uid"]))
assert len(replies) == 1
assert replies[0]["content"] == "Reply to parent"
def test_delete_own_comment(app_server):
s, _ = _session()
title = f"del-cmt-{int(time.time() * 1000)}"
post_uid = _create_post(s, title)
s.post(
f"{BASE_URL}/comments/create",
data={
"content": "Delete me",
"target_type": "post",
"post_uid": post_uid,
"target_uid": post_uid,
},
allow_redirects=False,
)
comment = get_table("comments").find_one(target_uid=post_uid)
r = s.post(
f"{BASE_URL}/comments/delete/{comment['uid']}",
allow_redirects=False,
)
assert r.status_code in (302, 303)
assert get_table("comments").find_one(uid=comment["uid"]) is None
def test_delete_comment_requires_owner(app_server):
s_alice, _ = _session()
title = f"owner-test-{int(time.time() * 1000)}"
post_uid = _create_post(s_alice, title)
s_alice.post(
f"{BASE_URL}/comments/create",
data={
"content": "Alice comment",
"target_type": "post",
"post_uid": post_uid,
"target_uid": post_uid,
},
allow_redirects=False,
)
comment = get_table("comments").find_one(target_uid=post_uid)
s_bob, _ = _session()
r = s_bob.post(
f"{BASE_URL}/comments/delete/{comment['uid']}",
allow_redirects=False,
)
assert r.status_code in (302, 303)
assert get_table("comments").find_one(uid=comment["uid"]) is not None
def test_create_comment_requires_login(app_server):
title = f"anon-cmt-{int(time.time() * 1000)}"
s, name = _session()
post_uid = _create_post(s, title)
anon = requests.Session()
r = anon.post(
f"{BASE_URL}/comments/create",
data={
"content": "Anon comment",
"target_type": "post",
"post_uid": post_uid,
"target_uid": post_uid,
},
allow_redirects=False,
)
assert r.status_code == 303
assert get_table("comments").find_one(target_uid=post_uid) is None
def test_comment_form_submission_ui(alice, app_server):
page, user = alice
title = f"ui-comment-{int(time.time() * 1000)}"
s = requests.Session()
s.post(
f"{BASE_URL}/auth/login",
data={"email": user["email"], "password": user["password"]},
)
r = s.post(
f"{BASE_URL}/posts/create",
data={"content": "UI comment target.", "title": title, "topic": "devlog"},
allow_redirects=False,
)
slug = r.headers["location"].split("/posts/")[-1]
page.goto(f"{BASE_URL}/posts/{slug}", wait_until="domcontentloaded")
textarea = page.locator("form.comment-form textarea[name='content']").first
textarea.wait_for(state="visible", timeout=5000)
textarea.fill("UI test comment")
page.locator("button.comment-form-submit").first.click()
page.wait_for_timeout(1000)
assert page.is_visible("text=UI test comment")
+49
View File
@@ -0,0 +1,49 @@
import requests
from tests.conftest import BASE_URL
from devplacepy.database import get_table
def test_containers_admin_page_requires_login(app_server):
r = requests.get(f"{BASE_URL}/admin/containers", allow_redirects=False)
assert r.status_code in (302, 303)
def test_containers_admin_page_loads(alice, app_server):
page, user = alice
page.goto(f"{BASE_URL}/admin/containers", wait_until="domcontentloaded")
assert page.is_visible(".admin-toolbar h2:has-text('Containers')")
def test_containers_admin_data_returns_json(alice, seeded_db):
s = requests.Session()
s.post(
f"{BASE_URL}/auth/login",
data={
"email": seeded_db["alice"]["email"],
"password": seeded_db["alice"]["password"],
},
)
r = s.get(f"{BASE_URL}/admin/containers/data")
assert r.status_code == 200
data = r.json()
assert "instances" in data
assert isinstance(data["instances"], list)
def test_containers_admin_data_denied_for_non_admin(app_server, seeded_db):
s = requests.Session()
s.post(
f"{BASE_URL}/auth/login",
data={
"email": seeded_db["bob"]["email"],
"password": seeded_db["bob"]["password"],
},
)
r = s.get(f"{BASE_URL}/admin/containers/data", allow_redirects=False)
assert r.status_code in (302, 303, 403)
def test_container_instance_page_404_for_missing(alice, app_server):
page, _ = alice
page.goto(f"{BASE_URL}/admin/containers/nonexistent", wait_until="domcontentloaded")
assert page.is_visible("text=Not Found") or page.is_visible("text=404")
+8
View File
@@ -224,3 +224,11 @@ def test_orphan_running_recovered_on_enable(fork_env):
job = queue.get_job(uid)
assert job["status"] == "pending"
assert job["retry_count"] == 1
def test_fork_status_http_unknown_returns_404(app_server):
import requests
from tests.conftest import BASE_URL
r = requests.get(f"{BASE_URL}/forks/nonexistent-uid")
assert r.status_code == 404
+109
View File
@@ -0,0 +1,109 @@
import io
import uuid
import requests
from PIL import Image
from tests.conftest import BASE_URL
from devplacepy.database import get_table
AJAX = {"X-Requested-With": "fetch"}
def _session():
s = requests.Session()
name = f"med_{uuid.uuid4().hex[:10]}"
s.post(
f"{BASE_URL}/auth/signup",
data={
"username": name,
"email": f"{name}@test.dev",
"password": "secret123",
"confirm_password": "secret123",
},
allow_redirects=True,
)
return s, name
def _png_bytes():
buf = io.BytesIO()
Image.new("RGB", (4, 4), (255, 0, 0)).save(buf, "PNG")
return buf.getvalue()
def _upload(s):
r = s.post(
f"{BASE_URL}/uploads/upload",
files={"file": ("a.png", _png_bytes(), "image/png")},
)
assert r.status_code == 201, r.text
return r.json()["uid"]
def test_delete_own_media(app_server):
s, name = _session()
uid = _upload(s)
r = s.post(f"{BASE_URL}/media/{uid}/delete", allow_redirects=False)
assert r.status_code in (302, 303)
attachment = get_table("attachments").find_one(uid=uid)
assert attachment is not None
assert attachment.get("deleted_at") is not None
def test_delete_others_media_denied(app_server):
s_alice, _ = _session()
uid = _upload(s_alice)
s_bob, _ = _session()
r = s_bob.post(f"{BASE_URL}/media/{uid}/delete", allow_redirects=False)
assert r.status_code in (302, 303)
attachment = get_table("attachments").find_one(uid=uid)
assert attachment.get("deleted_at") is None
def test_delete_nonexistent_media(app_server):
s, _ = _session()
r = s.post(f"{BASE_URL}/media/nonexistent-uid/delete", allow_redirects=False)
assert r.status_code in (302, 303, 404)
def test_admin_can_delete_any_media(app_server, seeded_db):
s_user, _ = _session()
uid = _upload(s_user)
s_admin = requests.Session()
s_admin.post(
f"{BASE_URL}/auth/login",
data={
"email": seeded_db["alice"]["email"],
"password": seeded_db["alice"]["password"],
},
)
r = s_admin.post(f"{BASE_URL}/media/{uid}/delete", allow_redirects=False)
assert r.status_code in (302, 303)
attachment = get_table("attachments").find_one(uid=uid)
assert attachment.get("deleted_at") is not None
def test_restore_media_requires_admin(app_server, seeded_db):
s_user, _ = _session()
uid = _upload(s_user)
s_admin = requests.Session()
s_admin.post(
f"{BASE_URL}/auth/login",
data={
"email": seeded_db["alice"]["email"],
"password": seeded_db["alice"]["password"],
},
)
s_admin.post(f"{BASE_URL}/media/{uid}/delete", allow_redirects=False)
attachment = get_table("attachments").find_one(uid=uid)
assert attachment.get("deleted_at") is not None
r = s_admin.post(f"{BASE_URL}/media/{uid}/restore", allow_redirects=False)
assert r.status_code in (302, 303)
attachment = get_table("attachments").find_one(uid=uid)
assert attachment.get("deleted_at") is None
def test_restore_media_denied_for_non_admin(app_server):
s, _ = _session()
uid = _upload(s)
r = s.post(f"{BASE_URL}/media/{uid}/restore", allow_redirects=False)
assert r.status_code in (302, 303, 403)
+36 -23
View File
@@ -175,39 +175,47 @@ def test_streaming_emits_sse(local_db, monkeypatch):
def test_authorize_static_access_key(local_db):
set_setting("gateway_require_auth", "1")
set_setting("gateway_access_key", "topsecret")
svc = GatewayService()
assert svc.authorize(_make_request(headers={"X-API-KEY": "topsecret"})) is True
assert svc.authorize(_make_request(headers={"X-API-KEY": "wrong"})) is False
set_setting("gateway_access_key", "")
try:
svc = GatewayService()
assert svc.authorize(_make_request(headers={"X-API-KEY": "topsecret"})) is True
assert svc.authorize(_make_request(headers={"X-API-KEY": "wrong"})) is False
finally:
set_setting("gateway_access_key", "")
set_setting("gateway_require_auth", "1")
def test_authorize_admin_and_user_toggles(local_db):
set_setting("gateway_require_auth", "1")
set_setting("gateway_access_key", "")
_, admin_key = _make_admin(role="Admin")
_, member_key = _make_admin(role="Member")
svc = GatewayService()
set_setting("gateway_allow_admins", "1")
set_setting("gateway_allow_users", "0")
assert (
svc.authorize(_make_request(headers={"Authorization": f"Bearer {admin_key}"}))
is True
)
assert svc.authorize(_make_request(headers={"X-API-KEY": member_key})) is False
try:
_, admin_key = _make_admin(role="Admin")
_, member_key = _make_admin(role="Member")
svc = GatewayService()
set_setting("gateway_allow_users", "1")
assert svc.authorize(_make_request(headers={"X-API-KEY": member_key})) is True
assert (
svc.authorize(_make_request(headers={"Authorization": f"Bearer {admin_key}"}))
is True
)
assert svc.authorize(_make_request(headers={"X-API-KEY": member_key})) is False
set_setting("gateway_allow_admins", "1")
set_setting("gateway_allow_users", "0")
set_setting("gateway_allow_users", "1")
assert svc.authorize(_make_request(headers={"X-API-KEY": member_key})) is True
finally:
set_setting("gateway_allow_admins", "1")
set_setting("gateway_allow_users", "1")
set_setting("gateway_require_auth", "1")
set_setting("gateway_access_key", "")
def test_authorize_require_auth_off_is_open(local_db):
set_setting("gateway_require_auth", "0")
svc = GatewayService()
assert svc.authorize(_make_request()) is True
set_setting("gateway_require_auth", "1")
try:
svc = GatewayService()
assert svc.authorize(_make_request()) is True
finally:
set_setting("gateway_require_auth", "1")
# ---------- HTTP behavior (admin configures via the Services tab endpoints) ----------
@@ -220,8 +228,13 @@ def _config(page, **fields):
def test_gateway_disabled_returns_503(alice):
page, _ = alice
page.request.post(f"{BASE_URL}/admin/services/openai/stop")
r = requests.post(f"{BASE_URL}/openai/v1/chat/completions", json={"messages": []})
assert r.status_code == 503
try:
r = requests.post(
f"{BASE_URL}/openai/v1/chat/completions", json={"messages": []}
)
assert r.status_code == 503
finally:
page.request.post(f"{BASE_URL}/admin/services/openai/start")
def test_gateway_auth_and_routing(alice):
@@ -256,4 +269,4 @@ def test_gateway_auth_and_routing(alice):
assert with_admin.status_code == 502
finally:
_config(page, gateway_access_key="")
page.request.post(f"{BASE_URL}/admin/services/openai/stop")
page.request.post(f"{BASE_URL}/admin/services/openai/start")
+3 -2
View File
@@ -381,8 +381,9 @@ def test_readonly_action_requires_confirmation():
assert confirmation_error("project_set_readonly", {"confirm": "true"}) is None
def test_private_action_needs_no_confirmation():
assert confirmation_error("project_set_private", {"value": "true"}) is None
def test_private_action_requires_confirmation():
assert confirmation_error("project_set_private", {"value": "true"}) is not None
assert confirmation_error("project_set_private", {"value": "true", "confirm": "true"}) is None
assert confirmation_error("vote", {}) is None
+28
View File
@@ -0,0 +1,28 @@
import requests
from tests.conftest import BASE_URL
def test_proxy_unknown_slug_returns_404(app_server):
r = requests.get(f"{BASE_URL}/p/nonexistent-slug", allow_redirects=False)
assert r.status_code == 404
def test_proxy_unknown_slug_json(app_server):
r = requests.get(
f"{BASE_URL}/p/nonexistent-slug",
headers={"X-Requested-With": "fetch"},
allow_redirects=False,
)
assert r.status_code == 404
def test_proxy_unknown_slug_with_path(app_server):
r = requests.get(
f"{BASE_URL}/p/nonexistent-slug/some/path", allow_redirects=False
)
assert r.status_code == 404
def test_proxy_post_unknown_slug(app_server):
r = requests.post(f"{BASE_URL}/p/nonexistent-slug", allow_redirects=False)
assert r.status_code == 404
+10 -5
View File
@@ -10,10 +10,15 @@ def _open_project_with_delete(page, title):
return proj_url, slug
def _click_delete(page):
page.locator(".project-actions-more").click()
page.locator(".context-menu-item:has-text('Delete')").click()
def test_data_confirm_opens_custom_dialog(alice):
page, _ = alice
_open_project_with_delete(page, "Confirm Dialog Open")
page.locator("button[data-confirm='Delete this project?']").click()
_click_delete(page)
expect(page.locator(".dialog-overlay.visible")).to_be_visible()
expect(page.locator(".dialog-overlay.visible .dialog-message")).to_contain_text(
"Delete"
@@ -23,7 +28,7 @@ def test_data_confirm_opens_custom_dialog(alice):
def test_data_confirm_accept_proceeds(alice):
page, _ = alice
proj_url, _ = _open_project_with_delete(page, "Confirm Accept")
page.locator("button[data-confirm='Delete this project?']").click()
_click_delete(page)
page.locator(".dialog-overlay.visible .dialog-confirm").click()
page.wait_for_url(f"{BASE_URL}/projects", wait_until="domcontentloaded")
resp = page.goto(proj_url, wait_until="domcontentloaded")
@@ -33,7 +38,7 @@ def test_data_confirm_accept_proceeds(alice):
def test_data_confirm_cancel_aborts(alice):
page, _ = alice
proj_url, _ = _open_project_with_delete(page, "Confirm Cancel")
page.locator("button[data-confirm='Delete this project?']").click()
_click_delete(page)
page.locator(".dialog-overlay.visible .dialog-cancel").click()
expect(page.locator(".dialog-overlay")).not_to_be_visible()
resp = page.goto(proj_url, wait_until="domcontentloaded")
@@ -43,7 +48,7 @@ def test_data_confirm_cancel_aborts(alice):
def test_dialog_escape_cancels(alice):
page, _ = alice
_open_project_with_delete(page, "Confirm Escape")
page.locator("button[data-confirm='Delete this project?']").click()
_click_delete(page)
expect(page.locator(".dialog-overlay.visible")).to_be_visible()
page.keyboard.press("Escape")
expect(page.locator(".dialog-overlay")).not_to_be_visible()
@@ -52,7 +57,7 @@ def test_dialog_escape_cancels(alice):
def test_dialog_backdrop_click_cancels(alice):
page, _ = alice
_open_project_with_delete(page, "Confirm Backdrop")
page.locator("button[data-confirm='Delete this project?']").click()
_click_delete(page)
overlay = page.locator(".dialog-overlay.visible")
expect(overlay).to_be_visible()
overlay.click(position={"x": 5, "y": 5})
+3 -3
View File
@@ -2,7 +2,7 @@ from devplacepy.database import get_table
from devplacepy.utils import (
award_badge,
award_xp,
badge_info,
get_badge,
check_milestone_badges,
create_mention_notifications,
extract_mentions,
@@ -111,13 +111,13 @@ def test_level_for_xp_negative():
def test_badge_info_known():
meta = badge_info("Star Author")
meta = get_badge("Star Author")
assert meta["icon"]
assert meta["description"] == "Earned 100 stars"
def test_badge_info_unknown_fallback():
meta = badge_info("Nonexistent Badge")
meta = get_badge("Nonexistent Badge")
assert meta["icon"]
assert meta["description"] == "Nonexistent Badge"
+2 -1
View File
@@ -303,7 +303,8 @@ def test_detail_download_button_downloads(app_server, page):
_write(key, slug, "README.md", "# hi")
_login(page, name)
page.goto(f"{BASE_URL}/projects/{slug}", wait_until="domcontentloaded")
btn = page.locator("button:has-text('Download zip')")
page.locator(".project-actions-more").click()
btn = page.locator(".context-menu-item:has-text('Download zip')")
btn.wait_for(state="visible")
with page.expect_download(timeout=30000) as dl_info:
btn.click()