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
@@ -0,0 +1,66 @@
|
||||
"""A work item's place in the loop (L1), kept in its row in the state store.
|
||||
|
||||
The row's ``position`` is the item's state; what the loop needs besides
|
||||
(the thread its conversation lives in, the revision of what is put up for
|
||||
agreement, what it is waiting for) is in ``data["loop"]``.
|
||||
"""
|
||||
|
||||
from dataclasses import asdict, dataclass
|
||||
from typing import Any
|
||||
|
||||
from nisse_dev.state import StateStore, WorkItem
|
||||
|
||||
READY = "ready"
|
||||
RUNNING = "running" # a driver run is under way; on boot it means interrupted
|
||||
WAITING = "waiting"
|
||||
CLOSED = "closed"
|
||||
|
||||
# What a waiting item waits for.
|
||||
GO_AHEAD = "go-ahead" # a go on the restatement up (L3, D1)
|
||||
ANSWER = "answer" # an answer to a question
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Loop:
|
||||
thread: str = "" # the root of the item's Matrix thread (L5)
|
||||
issue: int = 0 # its record issue (D1)
|
||||
wait: str = "" # GO_AHEAD / ANSWER, when waiting
|
||||
rev: int = 0 # the latest proposal put up (L3)
|
||||
started: bool = False # scope agreed: a go-ahead was given (D1)
|
||||
note: str = "" # what a waiting item said it is waiting on
|
||||
|
||||
def to_data(self) -> dict[str, Any]:
|
||||
return asdict(self)
|
||||
|
||||
|
||||
def loop_of(item: WorkItem) -> Loop:
|
||||
return Loop(**item.data.get("loop", {}))
|
||||
|
||||
|
||||
def save(
|
||||
store: StateStore, item_id: str, state: str, loop: Loop, *, cause: str
|
||||
) -> None:
|
||||
item = store.work_item(item_id)
|
||||
assert item is not None
|
||||
store.update_work_item(
|
||||
item_id,
|
||||
position=state,
|
||||
data={**item.data, "loop": loop.to_data()},
|
||||
cause=cause,
|
||||
)
|
||||
|
||||
|
||||
def item_for_thread(store: StateStore, thread: str) -> WorkItem | None:
|
||||
for item in store.work_items("open"):
|
||||
if loop_of(item).thread == thread:
|
||||
return item
|
||||
return None
|
||||
|
||||
|
||||
def item_for_issue(store: StateStore, issue: int) -> WorkItem | None:
|
||||
"""Any item, open or closed, recorded by this issue: intake never takes
|
||||
an issue twice (D1)."""
|
||||
for item in store.work_items():
|
||||
if loop_of(item).issue == issue:
|
||||
return item
|
||||
return None
|
||||
Reference in new issue
Block a user