]> git.giacomolli.uk - onechat/blob - README.rst
Escape tags in the Markdown of rich replies
[onechat] / README.rst
1 #########
2 OneChat
3 #########
4
5 OneChat is one AI agent in a single chat that never ends. A daemon owns
6 the agent and its files, and a terminal client and a Telegram bot talk
7 to it. It is in early development. Linux is supported.
8
9 ****************
10 Data directory
11 ****************
12
13 OneChat keeps its data and its token file in one directory. It uses the
14 first of these:
15
16 #. ``ONECHAT_DIR``, when it is set.
17 #. ``$XDG_DATA_HOME/onechat``, when ``XDG_DATA_HOME`` is an absolute
18 path. A relative value is ignored.
19 #. ``~/.local/share/onechat``. With no home directory, OneChat exits
20 with an error.
21
22 OneChat creates the directory with mode 0700.
23
24 ***************
25 Configuration
26 ***************
27
28 ``config.json`` in the data directory holds the settings. OneChat does
29 not create it. A missing file or field takes the default, and an unknown
30 field is an error.
31
32 .. code:: json
33
34 {
35 "endpoint": "chatgpt",
36 "compactor": {"model": "gpt-6.1-sol", "effort": "high"}
37 }
38
39 ``endpoint``
40 ``chatgpt`` (the default) sends model calls to the ChatGPT backend
41 with the subscription login. ``api`` sends them to the OpenAI API
42 with the key in ``OPENAI_API_KEY``, which must then be set. With
43 ``chatgpt``, OneChat ignores ``OPENAI_API_KEY``.
44
45 ``compactor``
46 The model and the reasoning effort of the summaries. The defaults are
47 ``gpt-6.1-sol`` and ``high``.
48
49 *******
50 Login
51 *******
52
53 The ``chatgpt`` endpoint needs a login with a ChatGPT account:
54
55 .. code:: console
56
57 onechat login
58
59 The command shows a link and a code. Open the link on any device, enter
60 the code, and approve the login. Device code login must be allowed in
61 the ChatGPT security settings. OneChat writes the tokens to
62 ``auth.json`` in the data directory, with mode 0600, and refreshes them
63 before they expire. The file belongs to OneChat only. Do not share it
64 with the Codex CLI, because each program rotates the refresh token and
65 logs the other out.
66
67 *******
68 Agent
69 *******
70
71 Start the daemon, and then the client in another terminal:
72
73 .. code:: console
74
75 onechat daemon
76 onechat chat
77
78 The daemon owns the root agent in ``agent/`` in the data directory and
79 listens on the Unix socket ``onechat.sock`` there. In the client, each
80 line is a message to the agent, the line ``/stop`` interrupts the
81 running turn, and Ctrl-D ends the client. The client shows the replies,
82 the tool calls, and the first lines of each tool result as they happen.
83 It also shows each message that the user sends from another client, with
84 the client as a mark, such as ``[telegram] text``. The daemon is the
85 only process that uses the token file while it runs, so ``onechat
86 login`` and ``onechat probe`` refuse to start until it stops.
87
88 The agent runs commands with bash as the user in its working directory,
89 with no sandbox. It has the rights of the user, also on the token file.
90
91 ``agent/config.json`` holds the settings of the agent. The first start
92 writes it with the defaults and a random ID. Do not change the ID: the
93 prompt cache key comes from it.
94
95 .. code:: json
96
97 {
98 "name": "OneChat",
99 "id": "0123456789abcdef0123456789abcdef",
100 "model": "gpt-6.1-sol",
101 "effort": "medium",
102 "workdir": "/home/user"
103 }
104
105 ``workdir`` is an absolute path, and its default is the home directory.
106 ``agent/instructions.md`` holds the user's instructions: who they are,
107 how their files are organized, and how they want work done. The prompt
108 ends with them, and a change applies at the next call.
109
110 **********
111 Telegram
112 **********
113
114 The daemon also connects the agent to a Telegram bot when
115 ``telegram.json`` exists in the data directory. It polls the Bot API for
116 new messages, so it needs no open port.
117
118 #. Make a bot with BotFather and keep its token.
119
120 #. Find your numeric Telegram user ID.
121
122 #. Write the token and the ID to ``telegram.json``, and give the file
123 mode 0600, because the token is a secret. The daemon refuses the file
124 with any other mode.
125
126 #. Start the daemon, open a private chat with the bot, and write.
127
128 .. code:: json
129
130 {"token": "123456:ABC-token-from-BotFather", "owner": 123456789}
131
132 The bot reads only the private chat of the owner and ignores all other
133 updates. Each text message goes to the agent as a user message, and the
134 line ``/stop`` interrupts the running turn. The bot sends the replies in
135 parts of at most 4096 characters, with their Markdown as Telegram
136 formats: bold, italic, strikethrough, code, code blocks, links, and
137 headings as bold text. When Telegram refuses the formats of a part, the
138 bot sends the part again as plain text. Each error goes as a short
139 message in plain text. It shows the typing action while a turn runs. It
140 does not send the tool calls. It sends each message that the user types
141 in the terminal client, with the mark ``[terminal]``.
142
143 ``agent/telegram-offset.json`` holds the ID of the next update. The bot
144 moves it past an update only after the message is durable in the log, so
145 a restart of the daemon does not lose a message. A crash just after the
146 write of a message can make the message repeat once. A network failure
147 or a rate limit makes the bot wait and try again, and does not stop the
148 daemon.
149
150 *******
151 Probe
152 *******
153
154 ``onechat probe LOG COUNT [BUILDS]`` measures the compactor on a log. It
155 appends the first COUNT messages of LOG, one JSON object ``{"kind",
156 "text"}`` on each line, to a new chat in ``probe/`` in the data
157 directory, and builds its tree with real model calls. The calls use the
158 quota of the account. The run stops at the first failed call, after 660
159 calls, or when the used percent of a quota window grows by more than 15
160 points. At the end it reports the calls per message, the cached share of
161 the input tokens, the retries, the line sizes, and the quota at the
162 first and the last call. BUILDS, from 1 to 8, limits the model calls at
163 once in the run.
164
165 A test writes a generated log of readable made-up messages:
166
167 .. code:: console
168
169 ONECHAT_PROBE_LOG=probe.jsonl go test -run TestProbeLogHasTheSyntheticMix
170
171 *************
172 Development
173 *************
174
175 Nix supplies the Go toolchain and all tools. Enter the development shell
176 with direnv:
177
178 .. code:: console
179
180 direnv allow
181
182 Or enter it directly:
183
184 .. code:: console
185
186 nix develop
187
188 The shell sets ``ONECHAT_DIR`` to ``.devenv/onechat`` in the checkout,
189 unless it is already set. Development data goes there, and Git ignores
190 ``.devenv``.
191
192 Build and run:
193
194 .. code:: console
195
196 nix build
197 ./result/bin/onechat
198
199 Format and check all files:
200
201 .. code:: console
202
203 bin/format
204 bin/check
205 nix flake check