X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/900ec0e7dad7a913190fcef9016e53495de9bdf8..c3c7bc90eb73e83f48fbeac6be149ade95a726c0:/README.rst diff --git a/README.rst b/README.rst index 9893fc4..473d1e5 100644 --- a/README.rst +++ b/README.rst @@ -82,15 +82,19 @@ The daemon owns the root agent in ``agent/`` in the data directory and listens on the Unix socket ``onechat.sock`` there. In the client, each line is a message to the agent, the line ``/stop`` interrupts the running turn, the line ``/stop NAME`` stops the subagent NAME, and -Ctrl-D ends the client. The client shows the replies, the tool calls, -and the first lines of each tool result as they happen. It also shows -each message that the user sends from another client, with the client as -a mark, such as ``[telegram] text``. When a subagent starts, the client -shows ``[NAME] started``, and it shows the whole report of the subagent, -which starts with ``[NAME]``, but not the tool calls of the subagent. -The daemon is the only process that uses the token file while it runs, -so ``onechat login`` and ``onechat probe`` refuse to start until it -stops. +Ctrl-D ends the client. On terminal output, the client shows ``> `` +after each event while idle and clears it before the next event. An +event can interrupt a line while the user types. The client shows the +replies, the tool calls, and the first lines of each tool result as they +happen. Replies and subagent reports render Markdown with styles and +wrapped tables only when the output is a terminal, and stay unchanged in +a pipe or file. It also shows each message that the user sends from +another client, with the client as a mark, such as ``[telegram] text``. +When a subagent starts, the client shows ``[NAME] started``, and it +shows the whole report of the subagent, which starts with ``[NAME]``, +but not the tool calls of the subagent. The daemon is the only process +that uses the token file while it runs, so ``onechat login`` and +``onechat probe`` refuse to start until it stops. The agent runs commands with bash as the user in its working directory, with no sandbox. It has the rights of the user, also on the token file. @@ -134,6 +138,23 @@ ends with them, and a change applies at the next call. The agent can propose a change to the file, and it makes the change only after the user agrees. +A permanent child agent lives in ``agent/children/NAME/`` with the same +files as the root. Create its directory and ``config.json`` by hand +before startup. Use the root config fields with a separate random ID, +set ``name`` to ``NAME``, and add ``parent`` with the root name and +``responsibility`` with the child's area. The daemon opens all children +at startup. A missing or invalid child config stops startup. Only one +level of children is supported. + +Each child has its own memory, model client, usage file, subagents, +schedules, and turn loop. Its prompt limits work to its area, asks for +only reports that need the parent or the user, and permits an empty +reply. Children have no WhatsApp tools and draft replies instead of +sending them. Clients and WhatsApp still address only the root. There +are no messages or tools between parent and child agents. A child's +saved schedules and subagents can resume at startup. The ready log line +lists the child names. + ************** User service ************** @@ -219,6 +240,128 @@ login. To stop it, or also remove its start at login: systemctl --user stop onechat.service systemctl --user disable onechat.service +******************** + Backup and restore +******************** + +Back up the running daemon to one mode 0600 ``.tar.gz`` archive. The +command prints its path and size in bytes. With no daemon, it fails. + +.. code:: console + + onechat backup BACKUP_FILE.tar.gz + ONECHAT_DIR=EMPTY_DATA_DIR onechat restore BACKUP_FILE.tar.gz + +The daemon flushes memory and holds its write locks during the agent +copy. It also flushes and locks the child agents and all subagent logs. +It copies the WhatsApp session with SQLite ``VACUUM INTO``, not as raw +database files. It then compresses the copy and installs the archive +through a temporary file and a rename. Use a backup path outside the +data directory. + +The archive includes configuration, instructions, the log, tree, +indexes, views, layout, schedules, subagents, children, Telegram offset, +tokens, and WhatsApp state and session. It excludes sockets, lock files, +``probe/``, and the ``agent-backup-*`` import copies. Links and special +files fail, rather than enter the archive. **The archive holds +secrets**, including login tokens, the Telegram bot token, and the +WhatsApp link. Keep it private. There is no encryption or remote copy. + +Restore needs a stopped daemon and an empty destination. It checks the +token lock and validates the archive before it installs files, with +their modes. It rejects unsafe paths, links, and special files. A failed +restore can leave ``auth.lock`` in the destination. Use another empty +directory for a retry. Do not run the source and restored WhatsApp links +at the same time. Check the restored log and view before you use it in +place of the source. + +For a daily local archive with the last 14 kept, save this script as +``BACKUP_SCRIPT``. Replace ``BACKUP_DIR`` with a private archive +directory outside the data directory. Replace ``ONECHAT_BINARY`` with +the absolute path through the stable ``result-service/bin/onechat`` +package link. Make the archive directory with mode 0700 before you +enable the timer. + +.. code:: sh + + #!/bin/sh + set -eu + umask 077 + cd BACKUP_DIR + ONECHAT_BINARY backup "onechat-$(date +%F).tar.gz" + find . -maxdepth 1 -type f -name 'onechat-????-??-??.tar.gz' -printf '%f\n' | + sort -r | tail -n +15 | + while IFS= read -r file; do + rm -- "$file" + done + +Save these units as ``onechat-backup.service`` and +``onechat-backup.timer`` in the systemd user unit directory. Replace +``DATA_DIR`` and ``BACKUP_SCRIPT`` with absolute paths. The service uses +the daemon unit from User service above. + +.. code:: ini + + [Unit] + Description=Back up OneChat data + Requires=onechat.service + After=onechat.service + + [Service] + Type=oneshot + Environment="ONECHAT_DIR=DATA_DIR" + UMask=0077 + ExecStart=/bin/sh BACKUP_SCRIPT + +.. code:: ini + + [Unit] + Description=Back up OneChat each day + + [Timer] + OnCalendar=daily + Persistent=true + + [Install] + WantedBy=timers.target + +Enable the timer with the user manager. A failed backup leaves the +existing archives in place and skips removal of old archives. + +.. code:: console + + systemctl --user daemon-reload + systemctl --user enable --now onechat-backup.timer + systemctl --user list-timers onechat-backup.timer + +************ + Daemon log +************ + +The daemon writes short events with the part prefix and ordered +``key=value`` attributes. An error cause comes last. Normal events use +info, failures that can be retried or lose nothing use warning, and +failures that lose data or stop work use error. + +With ``JOURNAL_STREAM`` set, each line carries a syslog priority without +a time stamp or level word. The user journal can show only warnings and +errors: + +.. code:: console + + journalctl --user -u onechat -p warning + +The summary warning names its node. A schedule run has ``late`` only +when it ran late: + +.. code:: text + + <4>onechat: summary failed level=0 index=0: fake failure + <6>onechat: schedule ran id=1 + <6>onechat: schedule ran id=2 late=1m0s + +Outside the journal, each line starts with the time stamp and level. + ********** Telegram ********** @@ -401,13 +544,14 @@ exports before import: their text goes to the model as chat history. The import merges the full log in date order and builds its tree with real compactor calls, the agent prompt, and the agent cache key. It uses -the probe's 660-call and 15-quota-point limits and prints the same usage -report. Its estimate is about 1.3 calls per merged message, not a cost -guarantee. A stopped or failed run leaves the agent unchanged. A new run -starts over. Success keeps the other agent files and renames the old -directory to ``agent-backup-TIMESTAMP`` before it installs the new one. -Keep the backup until the new memory is checked, then start the daemon. -Message IDs change to their positions in the merged log. +a limit of 1,500 calls and the probe's 15-quota-point limit, and prints +the same usage report. Its estimate is about 1.3 calls per merged +message, not a cost guarantee. A stopped or failed run leaves the agent +unchanged. A new run starts over. Success keeps the other agent files +and renames the old directory to ``agent-backup-TIMESTAMP`` before it +installs the new one. Keep the backup until the new memory is checked, +then start the daemon. Message IDs change to their positions in the +merged log. *********** Schedules @@ -498,6 +642,9 @@ Build and run: nix build ./result/bin/onechat +``onechat version`` prints ``yyyy-mm-dd-gitshort`` for a Nix build, adds +``-dirty`` for a dirty tree, and prints ``dev`` for a direct Go build. + Format and check all files: .. code:: console