Hold resumed turns and public-link visitors to the right wiki rules

A paused turn is found by the agent owner's id, so anyone holding one of
the owner's agent keys could resume the owner's own chat with its saved
wiki edit rights. A resume now counts as an API or widget caller when
either the saved state or the resuming request is one, cuts the wiki tool
to wiki_view unless the wiki allows outside edits, and gives the tool
executor the same flags. A request that names an agent, by key or id, may
only resume that agent's turn; otherwise the claim is released and the
request refused.

Public-link visitors run as themselves and reach only wikis they may edit,
so the wiki switch no longer applies to them. Instead every wiki write in
a public-link run waits for the visitor's approval, so the agent owner's
prompt or sources can't steer an edit to the visitor's wiki unasked.
This commit is contained in:
arc53-machine committed 2026-09-29 16:37:58 +01:00
1 parent 91e70d2f0c
commit 051452c438
10 files changed
+405 -64

No files matched your search

@@ -119,7 +119,7 @@ class TestWikiConfigAccess:
class TestWikiOutsideEdits:
"""API, widget and public-link runs edit a wiki only when its owner allows."""
"""API-key runs edit a wiki only when its owner allows; link visitors are asked."""
def _tools(self, conn, caller, sid, agent_config):
from docsgpt.agents.tools.wiki import WIKI_TOOL_ID, add_wiki_tool
@@ -130,19 +130,44 @@ class TestWikiOutsideEdits:
proc.decoded_token = {"sub": caller}
proc.agent_config = agent_config
cfg = proc._build_wiki_config()
if cfg is None:
return None
tools = {}
add_wiki_tool(tools, cfg)
return {a["name"] for a in tools[WIKI_TOOL_ID]["actions"]}
return {a["name"]: bool(a.get("require_approval")) for a in tools[WIKI_TOOL_ID]["actions"]}
@pytest.mark.parametrize("flag", ["external_api_caller", "public_link_caller"])
def test_outside_caller_reads_until_the_owner_allows_edits(self, use_conn, flag):
sid = str(SourcesRepository(use_conn).create("W", user_id=OWNER, config={"kind": "wiki"})["id"])
assert self._tools(use_conn, OWNER, sid, {flag: True}) == {"wiki_view"}
def _wiki(self, conn, owner=OWNER):
return str(SourcesRepository(conn).create("W", user_id=owner, config={"kind": "wiki"})["id"])
def test_api_key_caller_reads_until_the_owner_allows_edits(self, use_conn):
# A widget or API run acts as the agent owner, who owns the wiki.
sid = self._wiki(use_conn)
assert self._tools(use_conn, OWNER, sid, {"external_api_caller": True}) == {"wiki_view": False}
SourcesRepository(use_conn).set_wiki_outside_edits(sid, OWNER, True)
assert "wiki_create" in self._tools(use_conn, OWNER, sid, {flag: True})
tools = self._tools(use_conn, OWNER, sid, {"external_api_caller": True})
assert tools["wiki_create"] is False
def test_owner_and_team_editor_keep_every_action(self, use_conn):
sid = str(SourcesRepository(use_conn).create("W", user_id=OWNER, config={"kind": "wiki"})["id"])
sid = self._wiki(use_conn)
_share(use_conn, "source", sid, "ed", "editor")
assert "wiki_create" in self._tools(use_conn, OWNER, sid, {})
assert "wiki_create" in self._tools(use_conn, "ed", sid, {})
assert self._tools(use_conn, OWNER, sid, {})["wiki_create"] is False
assert self._tools(use_conn, "ed", sid, {})["wiki_create"] is False
def test_link_visitor_edits_their_own_wiki_after_approving(self, use_conn):
# Visitor owns a wiki that the agent (someone else's) also uses.
sid = self._wiki(use_conn, owner="visitor")
tools = self._tools(use_conn, "visitor", sid, {"public_link_caller": True})
assert tools["wiki_view"] is False
assert tools["wiki_create"] is True and tools["wiki_delete"] is True
def test_link_visitor_who_edits_the_wiki_through_a_team(self, use_conn):
sid = self._wiki(use_conn)
_share(use_conn, "source", sid, "visitor", "editor")
tools = self._tools(use_conn, "visitor", sid, {"public_link_caller": True})
assert tools["wiki_create"] is True
def test_link_visitor_without_edit_rights_gets_no_wiki_tool(self, use_conn):
sid = self._wiki(use_conn)
assert self._tools(use_conn, "visitor", sid, {"public_link_caller": True}) is None
_share(use_conn, "source", sid, "visitor", "viewer")
assert self._tools(use_conn, "visitor", sid, {"public_link_caller": True}) is None