The new loop's core: intake, restatement, go-ahead, threads (M2.1, PR B)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m44s
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m44s
nisse_dev.loop runs alongside control.py, chosen with `loop: new`. Issues assigned to the agent (by an operator, or filed by one) become work items, each with a Matrix thread. A driver run is one model with tools until it calls a waiting tool: restate puts rev-N up for a go-ahead, bound to the revision on screen when the reply arrived; ask_human waits for an answer; close_item closes the item and its issue. Before the go-ahead the driver can look but not change or close anything. One item is active at a time; replies in an item's thread go to it. On boot, an item cut off mid-run waits on the operator, and waiting points are put up again. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
This commit is contained in:
1 parent
6dff36f458
commit
f84bb70839
14 files changed
+1205
-1
No files matched your search
@@ -42,6 +42,15 @@ class Comment:
|
||||
created_at: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Issue:
|
||||
number: int
|
||||
title: str
|
||||
body: str
|
||||
author: str
|
||||
labels: tuple[str, ...]
|
||||
|
||||
|
||||
class GiteaClient:
|
||||
def __init__(
|
||||
self,
|
||||
@@ -158,6 +167,44 @@ class GiteaClient:
|
||||
def close_pull_request(self, repo: str, index: int) -> None:
|
||||
self._request("PATCH", f"/repos/{repo}/pulls/{index}", json={"state": "closed"})
|
||||
|
||||
def assigned_issues(self, repo: str, assignee: str) -> list[Issue]:
|
||||
"""Open issues (not PRs) assigned to ``assignee``: intake (D1)."""
|
||||
data = self._request(
|
||||
"GET",
|
||||
f"/repos/{repo}/issues",
|
||||
params={
|
||||
"type": "issues",
|
||||
"state": "open",
|
||||
"assigned_by": assignee,
|
||||
"limit": 50,
|
||||
},
|
||||
)
|
||||
return [
|
||||
Issue(
|
||||
number=int(item["number"]),
|
||||
title=str(item.get("title") or ""),
|
||||
body=str(item.get("body") or ""),
|
||||
author=str(item["user"]["login"]),
|
||||
labels=tuple(str(label["name"]) for label in item.get("labels") or []),
|
||||
)
|
||||
for item in data or []
|
||||
if not item.get("pull_request")
|
||||
]
|
||||
|
||||
def assigned_by(self, repo: str, index: int, assignee: str) -> str | None:
|
||||
"""Who last assigned the issue to ``assignee``, from its timeline;
|
||||
None if the timeline doesn't say."""
|
||||
events = self._request("GET", f"/repos/{repo}/issues/{index}/timeline") or []
|
||||
who = None
|
||||
for event in events:
|
||||
if (
|
||||
event.get("type") == "assignees"
|
||||
and not event.get("removed_assignee")
|
||||
and (event.get("assignee") or {}).get("login") == assignee
|
||||
):
|
||||
who = (event.get("user") or {}).get("login")
|
||||
return who
|
||||
|
||||
def close_issue(self, repo: str, index: int) -> None:
|
||||
self._request(
|
||||
"PATCH", f"/repos/{repo}/issues/{index}", json={"state": "closed"}
|
||||
|
||||
Reference in new issue
Block a user