Compare commits
4
Commits
3e62529a0e
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6db6c7b6ec | ||
|
|
2bf2326178 | ||
|
|
1d8b8c68a3 | ||
|
|
fc97463f0f |
No files matched your search
@@ -31,6 +31,11 @@ ADMIN_PASS=
|
|||||||
# Подпись session-cookie admin UI
|
# Подпись session-cookie admin UI
|
||||||
SESSION_SECRET=
|
SESSION_SECRET=
|
||||||
|
|
||||||
|
# Мастер-ключ хранилища секретов (admin → System → Secrets): openssl rand -hex 32.
|
||||||
|
# Шифрует таблицу secrets в postgres, поэтому дамп базы без него закрыт.
|
||||||
|
# В env модели не попадает. Потерян или сменён - секреты задаются заново.
|
||||||
|
SECRETS_KEY=
|
||||||
|
|
||||||
### Claude ####################################################################
|
### Claude ####################################################################
|
||||||
|
|
||||||
# OAuth-токен подписки для Claude Agent SDK внутри контейнера.
|
# OAuth-токен подписки для Claude Agent SDK внутри контейнера.
|
||||||
|
|||||||
@@ -71,6 +71,12 @@ read-write (it is the dispatcher's home), and `beaver_agent/policy.py` keeps
|
|||||||
it out of `.obsidian`, `мета` outside `мета/бобер`, and from deleting
|
it out of `.obsidian`, `мета` outside `мета/бобер`, and from deleting
|
||||||
anything but its own files.
|
anything but its own files.
|
||||||
|
|
||||||
|
Keys the agent needs for its own scripts (an RPC key, a service account
|
||||||
|
file) go into **System → Secrets** in the admin, not into files in the
|
||||||
|
container: they are sealed with `SECRETS_KEY` in Postgres, never shown back,
|
||||||
|
and reach a command only through `secret run NAME -- cmd` (see the `secrets`
|
||||||
|
skill under `мета/бобер/скиллы/общие/`).
|
||||||
|
|
||||||
### 4. Mint a token
|
### 4. Mint a token
|
||||||
|
|
||||||
Open admin at `http://localhost:62990/` (or `https://<DOMAIN>/` if Caddy), sign in with `ADMIN_USER` / `ADMIN_PASS`, go to **Tokens → Create**, scope `*` for first run.
|
Open admin at `http://localhost:62990/` (or `https://<DOMAIN>/` if Caddy), sign in with `ADMIN_USER` / `ADMIN_PASS`, go to **Tokens → Create**, scope `*` for first run.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Ночью после ротации: глубокие чаты, тихие два дня, закрываются, не больше трёх."""
|
"""Ночью после ротации: тихие глубокие чаты - с выжимкой, тихие ветки - молча."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -21,3 +21,5 @@ async def close_idle(run: JobRun) -> None:
|
|||||||
result.digest.path if result.digest else None,
|
result.digest.path if result.digest else None,
|
||||||
result.error,
|
result.error,
|
||||||
)
|
)
|
||||||
|
for branch in await run.close_quiet(kind="branch", days=3):
|
||||||
|
_log.info("closed quiet branch %s «%s»", branch.external_id, branch.title)
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
"""Раз в час: пора ли сменить мастера (ночь, возраст, размер контекста)."""
|
"""Раз в час: пора ли сменить мастера (ночь, возраст, размер контекста).
|
||||||
|
|
||||||
|
Запуск руками (кнопка в админке, `POST /api/jobs/ротация/run`) ротирует мастера
|
||||||
|
сразу, с хендаутом: так новая сессия подхватывает свежие гранулы vault.
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -12,5 +16,6 @@ _log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
async def rotate(run: JobRun) -> None:
|
async def rotate(run: JobRun) -> None:
|
||||||
for master in await run.rotate():
|
force = "manual" if run.trigger == "manual" else None
|
||||||
|
for master in await run.rotate(force=force):
|
||||||
_log.info("rotated master -> %s", master.external_id)
|
_log.info("rotated master -> %s", master.external_id)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
"""`POST /hooks/t3code`: тред завершился, упал или спрашивает - срочно в мастер."""
|
"""`POST /hooks/t3code`: событие треда - срочно в разговор, который его запустил."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -15,7 +15,12 @@ QUESTION_HINT = (
|
|||||||
|
|
||||||
|
|
||||||
async def t3code_event(run: JobRun) -> None:
|
async def t3code_event(run: JobRun) -> None:
|
||||||
await run.inject_master(describe(run.payload), urgency="urgent", origin="t3code")
|
await run.inject_into(
|
||||||
|
run.payload.get("origin"),
|
||||||
|
describe(run.payload),
|
||||||
|
urgency="urgent",
|
||||||
|
origin="t3code",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def describe(p: dict[str, Any]) -> str:
|
def describe(p: dict[str, Any]) -> str:
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ SAME_DAY = (
|
|||||||
ROTATION_WHY = {
|
ROTATION_WHY = {
|
||||||
"age": "прошло больше 36 часов",
|
"age": "прошло больше 36 часов",
|
||||||
"context": "контекст стал слишком большим",
|
"context": "контекст стал слишком большим",
|
||||||
|
"manual": "Бобёр запустил ротацию руками",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from beaver_agent.jobs.t3code import t3code_event
|
||||||
|
|
||||||
|
|
||||||
|
class FakeRun:
|
||||||
|
def __init__(self, payload: dict[str, Any]) -> None:
|
||||||
|
self.payload = payload
|
||||||
|
self.injects: list[tuple[str | None, str, str, str]] = []
|
||||||
|
|
||||||
|
async def inject_into(
|
||||||
|
self, public_id: str | None, text: str, *, urgency: str, origin: str
|
||||||
|
) -> bool:
|
||||||
|
self.injects.append((public_id, text, urgency, origin))
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def test_the_event_goes_to_the_conversation_that_dispatched() -> None:
|
||||||
|
run = FakeRun(
|
||||||
|
{
|
||||||
|
"event": "completed",
|
||||||
|
"thread_id": "t1",
|
||||||
|
"title": "lint",
|
||||||
|
"machine": "mac",
|
||||||
|
"project": "beaver",
|
||||||
|
"origin": "conv-7",
|
||||||
|
"text": "done",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await t3code_event(run) # ty: ignore[invalid-argument-type]
|
||||||
|
[(target, text, urgency, origin)] = run.injects
|
||||||
|
assert (target, urgency, origin) == ("conv-7", "urgent", "t3code")
|
||||||
|
assert "завершён" in text and "done" in text
|
||||||
@@ -365,6 +365,7 @@ dependencies = [
|
|||||||
{ name = "argon2-cffi" },
|
{ name = "argon2-cffi" },
|
||||||
{ name = "claude-agent-sdk" },
|
{ name = "claude-agent-sdk" },
|
||||||
{ name = "croniter" },
|
{ name = "croniter" },
|
||||||
|
{ name = "cryptography" },
|
||||||
{ name = "fastapi" },
|
{ name = "fastapi" },
|
||||||
{ name = "fastmcp" },
|
{ name = "fastmcp" },
|
||||||
{ name = "greenlet" },
|
{ name = "greenlet" },
|
||||||
@@ -392,6 +393,7 @@ requires-dist = [
|
|||||||
{ name = "argon2-cffi", specifier = ">=25.1.0" },
|
{ name = "argon2-cffi", specifier = ">=25.1.0" },
|
||||||
{ name = "claude-agent-sdk", specifier = ">=0.2.157" },
|
{ name = "claude-agent-sdk", specifier = ">=0.2.157" },
|
||||||
{ name = "croniter", specifier = ">=6.2.4" },
|
{ name = "croniter", specifier = ">=6.2.4" },
|
||||||
|
{ name = "cryptography", specifier = ">=48.0.0" },
|
||||||
{ name = "fastapi", specifier = ">=0.136.1" },
|
{ name = "fastapi", specifier = ">=0.136.1" },
|
||||||
{ name = "fastmcp", specifier = ">=3.3.1" },
|
{ name = "fastmcp", specifier = ">=3.3.1" },
|
||||||
{ name = "greenlet", specifier = ">=3.5.0" },
|
{ name = "greenlet", specifier = ">=3.5.0" },
|
||||||
|
|||||||
Reference in new issue
Block a user