local-agent-chat 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. local_agent_chat-0.1.0/.env.example +28 -0
  2. local_agent_chat-0.1.0/CONTEXT.md +45 -0
  3. local_agent_chat-0.1.0/CONTRIBUTING.md +34 -0
  4. local_agent_chat-0.1.0/LICENSE +21 -0
  5. local_agent_chat-0.1.0/MANIFEST.in +6 -0
  6. local_agent_chat-0.1.0/PKG-INFO +146 -0
  7. local_agent_chat-0.1.0/README.md +109 -0
  8. local_agent_chat-0.1.0/SECURITY.md +13 -0
  9. local_agent_chat-0.1.0/docs/adr/0001-persistent-chat-boundaries.md +15 -0
  10. local_agent_chat-0.1.0/docs/adr/0002-local-persistence-behind-adapters.md +7 -0
  11. local_agent_chat-0.1.0/docs/adr/0003-isolated-command-execution.md +13 -0
  12. local_agent_chat-0.1.0/docs/adr/0004-bind-agent-mode-to-chat.md +15 -0
  13. local_agent_chat-0.1.0/docs/adr/0005-retrieve-global-memory-on-demand.md +9 -0
  14. local_agent_chat-0.1.0/docs/adr/0006-retry-llm-at-provider-boundary.md +13 -0
  15. local_agent_chat-0.1.0/docs/adr/0007-load-project-skills-from-versioned-packages.md +7 -0
  16. local_agent_chat-0.1.0/docs/adr/0008-keep-curated-long-term-memory-in-markdown.md +7 -0
  17. local_agent_chat-0.1.0/docs/adr/0009-scope-agent-file-reading-by-chat-mode.md +35 -0
  18. local_agent_chat-0.1.0/docs/adr/0010-coordinate-native-edits-with-current-history.md +7 -0
  19. local_agent_chat-0.1.0/docs/adr/0011-use-one-sandboxed-react-loop.md +5 -0
  20. local_agent_chat-0.1.0/docs/adr/0012-package-resources-and-separate-user-data.md +3 -0
  21. local_agent_chat-0.1.0/docs/architecture.md +61 -0
  22. local_agent_chat-0.1.0/docs/package-validation.md +34 -0
  23. local_agent_chat-0.1.0/docs/publishing.md +59 -0
  24. local_agent_chat-0.1.0/docs/react-validation.md +58 -0
  25. local_agent_chat-0.1.0/docs/research/chainlit-agent-ui-case-studies.md +268 -0
  26. local_agent_chat-0.1.0/docs/research/chainlit-interactive-ui.md +378 -0
  27. local_agent_chat-0.1.0/docs/research/global-agent-memory.md +298 -0
  28. local_agent_chat-0.1.0/docs/research/jupyter-vscode-proxy-root-path.md +116 -0
  29. local_agent_chat-0.1.0/local_agent_chat/__init__.py +1 -0
  30. local_agent_chat-0.1.0/local_agent_chat/__main__.py +4 -0
  31. local_agent_chat-0.1.0/local_agent_chat/agent_context.py +141 -0
  32. local_agent_chat-0.1.0/local_agent_chat/agent_events.py +126 -0
  33. local_agent_chat-0.1.0/local_agent_chat/agent_execution.py +201 -0
  34. local_agent_chat-0.1.0/local_agent_chat/agent_memory.py +84 -0
  35. local_agent_chat-0.1.0/local_agent_chat/app.py +494 -0
  36. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/config.toml +184 -0
  37. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/ar-SA.json +259 -0
  38. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/bn.json +260 -0
  39. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/da-DK.json +259 -0
  40. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/de-DE.json +254 -0
  41. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/el-GR.json +260 -0
  42. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/en-US.json +260 -0
  43. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/es.json +260 -0
  44. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/fr-FR.json +260 -0
  45. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/gu.json +260 -0
  46. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/he-IL.json +260 -0
  47. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/hi.json +260 -0
  48. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/it.json +254 -0
  49. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/ja.json +259 -0
  50. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/kn.json +260 -0
  51. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/ko.json +254 -0
  52. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/ml.json +260 -0
  53. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/mr.json +260 -0
  54. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/nl.json +260 -0
  55. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/pt-PT.json +260 -0
  56. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/ru-RU.json +260 -0
  57. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/ta.json +260 -0
  58. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/te.json +260 -0
  59. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/zh-CN.json +260 -0
  60. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit/translations/zh-TW.json +260 -0
  61. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit.md +5 -0
  62. local_agent_chat-0.1.0/local_agent_chat/assets/chainlit_ru-RU.md +3 -0
  63. local_agent_chat-0.1.0/local_agent_chat/assets/examples/env.example +28 -0
  64. local_agent_chat-0.1.0/local_agent_chat/assets/examples/models.example.yaml +10 -0
  65. local_agent_chat-0.1.0/local_agent_chat/assets/public/avatars/localchat.png +0 -0
  66. local_agent_chat-0.1.0/local_agent_chat/assets/public/branding.css +12 -0
  67. local_agent_chat-0.1.0/local_agent_chat/assets/public/favicon.png +0 -0
  68. local_agent_chat-0.1.0/local_agent_chat/assets/public/localchat-icon.png +0 -0
  69. local_agent_chat-0.1.0/local_agent_chat/assets/public/localchat-logo.png +0 -0
  70. local_agent_chat-0.1.0/local_agent_chat/assets/public/logo_dark.png +0 -0
  71. local_agent_chat-0.1.0/local_agent_chat/assets/public/logo_light.png +0 -0
  72. local_agent_chat-0.1.0/local_agent_chat/assets/public/proxy-method-override.js +29 -0
  73. local_agent_chat-0.1.0/local_agent_chat/auxiliary_labels.py +64 -0
  74. local_agent_chat-0.1.0/local_agent_chat/chainlit_data.py +764 -0
  75. local_agent_chat-0.1.0/local_agent_chat/chainlit_persistence.py +61 -0
  76. local_agent_chat-0.1.0/local_agent_chat/chainlit_revision.py +69 -0
  77. local_agent_chat-0.1.0/local_agent_chat/chainlit_stop.py +40 -0
  78. local_agent_chat-0.1.0/local_agent_chat/chainlit_ui.py +288 -0
  79. local_agent_chat-0.1.0/local_agent_chat/chainlit_uploads.py +109 -0
  80. local_agent_chat-0.1.0/local_agent_chat/chat_bindings.py +85 -0
  81. local_agent_chat-0.1.0/local_agent_chat/chat_titles.py +69 -0
  82. local_agent_chat-0.1.0/local_agent_chat/cli.py +327 -0
  83. local_agent_chat-0.1.0/local_agent_chat/installation.py +60 -0
  84. local_agent_chat-0.1.0/local_agent_chat/llm_retry.py +81 -0
  85. local_agent_chat-0.1.0/local_agent_chat/local_storage.py +56 -0
  86. local_agent_chat-0.1.0/local_agent_chat/prompts.py +31 -0
  87. local_agent_chat-0.1.0/local_agent_chat/proxy_prefix.py +46 -0
  88. local_agent_chat-0.1.0/local_agent_chat/runtime.py +218 -0
  89. local_agent_chat-0.1.0/local_agent_chat/sandbox_files.py +227 -0
  90. local_agent_chat-0.1.0/local_agent_chat/sandbox_tools.py +196 -0
  91. local_agent_chat-0.1.0/local_agent_chat/settings.py +198 -0
  92. local_agent_chat-0.1.0/local_agent_chat/sqlite_history.py +255 -0
  93. local_agent_chat-0.1.0/local_agent_chat/tool_logs.py +43 -0
  94. local_agent_chat-0.1.0/local_agent_chat.egg-info/PKG-INFO +146 -0
  95. local_agent_chat-0.1.0/local_agent_chat.egg-info/SOURCES.txt +130 -0
  96. local_agent_chat-0.1.0/local_agent_chat.egg-info/dependency_links.txt +1 -0
  97. local_agent_chat-0.1.0/local_agent_chat.egg-info/entry_points.txt +2 -0
  98. local_agent_chat-0.1.0/local_agent_chat.egg-info/requires.txt +15 -0
  99. local_agent_chat-0.1.0/local_agent_chat.egg-info/top_level.txt +1 -0
  100. local_agent_chat-0.1.0/models.example.yaml +10 -0
  101. local_agent_chat-0.1.0/pyproject.toml +77 -0
  102. local_agent_chat-0.1.0/scripts/check_distribution.py +476 -0
  103. local_agent_chat-0.1.0/scripts/run.sh +31 -0
  104. local_agent_chat-0.1.0/setup.cfg +4 -0
  105. local_agent_chat-0.1.0/tests/conftest.py +16 -0
  106. local_agent_chat-0.1.0/tests/test_agent_events.py +43 -0
  107. local_agent_chat-0.1.0/tests/test_agent_execution.py +387 -0
  108. local_agent_chat-0.1.0/tests/test_auxiliary_labels.py +141 -0
  109. local_agent_chat-0.1.0/tests/test_brand_assets.py +93 -0
  110. local_agent_chat-0.1.0/tests/test_chainlit_data.py +805 -0
  111. local_agent_chat-0.1.0/tests/test_chainlit_stop.py +138 -0
  112. local_agent_chat-0.1.0/tests/test_chainlit_translations.py +9 -0
  113. local_agent_chat-0.1.0/tests/test_chainlit_ui.py +297 -0
  114. local_agent_chat-0.1.0/tests/test_chainlit_uploads.py +131 -0
  115. local_agent_chat-0.1.0/tests/test_chat_bindings.py +50 -0
  116. local_agent_chat-0.1.0/tests/test_chat_runtime.py +493 -0
  117. local_agent_chat-0.1.0/tests/test_chat_title_lifecycle.py +177 -0
  118. local_agent_chat-0.1.0/tests/test_chat_titles.py +44 -0
  119. local_agent_chat-0.1.0/tests/test_cli.py +179 -0
  120. local_agent_chat-0.1.0/tests/test_cli_shutdown.py +86 -0
  121. local_agent_chat-0.1.0/tests/test_llm_retry.py +368 -0
  122. local_agent_chat-0.1.0/tests/test_local_storage.py +17 -0
  123. local_agent_chat-0.1.0/tests/test_model_profile_lifecycle.py +83 -0
  124. local_agent_chat-0.1.0/tests/test_proxy_prefix.py +73 -0
  125. local_agent_chat-0.1.0/tests/test_revision_lifecycle.py +459 -0
  126. local_agent_chat-0.1.0/tests/test_revision_uploads.py +148 -0
  127. local_agent_chat-0.1.0/tests/test_run_script.py +47 -0
  128. local_agent_chat-0.1.0/tests/test_sandbox_files.py +234 -0
  129. local_agent_chat-0.1.0/tests/test_sandbox_tools.py +114 -0
  130. local_agent_chat-0.1.0/tests/test_server_smoke.py +214 -0
  131. local_agent_chat-0.1.0/tests/test_settings.py +126 -0
  132. local_agent_chat-0.1.0/tests/test_sqlite_resources.py +50 -0
@@ -0,0 +1,28 @@
1
+ APP_PORT=8765
2
+ # APP_HOST=127.0.0.1
3
+ # APP_ROOT_PATH=/user/name/vscode/proxy/8765
4
+ APP_DATA_DIR=.local-agent-chat
5
+ MODEL_PROFILES_FILE=models.yaml
6
+ CHAINLIT_AUTH_SECRET=replace-with-a-random-secret-at-least-32-characters
7
+ OPENAI_BASE_URL=https://openrouter.ai/api/v1
8
+ OPENAI_API_KEY=replace-with-your-openrouter-api-key
9
+
10
+ # Additional attempts after the first LLM HTTP request (0-10); 0 disables retries.
11
+ LLM_MAX_RETRIES=3
12
+ # Timeout for one LLM HTTP request.
13
+ LLM_REQUEST_TIMEOUT_SECONDS=60
14
+ # Maximum pause between streamed chunks; a stream is terminal after its first chunk.
15
+ LLM_STREAM_CHUNK_TIMEOUT_SECONDS=120
16
+ # Additional model-handler attempts after a zero-chunk stream timeout (0-10).
17
+ LLM_STREAM_RETRIES=1
18
+ # Total timeout for one auxiliary Chat-title LLM call.
19
+ LLM_AUXILIARY_TIMEOUT_SECONDS=30
20
+
21
+ # ReAct context window, compaction, output and per-Turn inference budget.
22
+ # Keep the context budget within the actual window of the configured model.
23
+ AGENT_CONTEXT_TOKENS=16000
24
+ AGENT_SUMMARY_TRIGGER_TOKENS=10000
25
+ AGENT_KEEP_TOKENS=3000
26
+ AGENT_SUMMARY_TOKENS=1000
27
+ AGENT_MAX_OUTPUT_TOKENS=2000
28
+ AGENT_MAX_MODEL_CALLS=12
@@ -0,0 +1,45 @@
1
+ # LocalChat
2
+
3
+ Локальный чат-интерфейс для продолжительной работы пользователя с агентом в среде JupyterHub.
4
+
5
+ ## Language
6
+
7
+ **Chat**:
8
+ Продолжаемый диалог пользователя с агентом, имеющий собственную историю и память.
9
+ _Avoid_: Session, conversation
10
+
11
+ **Turn**:
12
+ Один пользовательский запрос и связанный с ним ответ агента.
13
+ _Avoid_: Message pair, interaction
14
+
15
+ **Agent**:
16
+ Участник чата, который отвечает пользователю, рассуждает и применяет доступные ему инструменты.
17
+ _Avoid_: Bot, assistant
18
+
19
+ **Sandbox**:
20
+ Принадлежащее одному Chat хранилище загруженных файлов, которые Agent может читать. Revision восстанавливает файлы, соответствующие изменяемому запросу.
21
+ _Avoid_: Workspace, project directory
22
+
23
+ **Uploaded File**:
24
+ Файл, явно переданный пользователем в чат и доступный агенту внутри Sandbox.
25
+ _Avoid_: Attachment, workspace file
26
+
27
+ **Chat History**:
28
+ Сохранённые чаты, которые пользователь может вновь открыть после перезапуска приложения.
29
+ _Avoid_: Logs, transcript archive
30
+
31
+ **Agent Memory**:
32
+ Контекст конкретного Chat: недавние сообщения и сводка более раннего диалога, необходимые Agent для продолжения работы.
33
+ _Avoid_: Chat History, context
34
+
35
+ **Model Profile**:
36
+ Выбранная при создании Chat конфигурация модели, не содержащая секретов провайдера. Пока профиль доступен, он неизменяем; удалённый профиль при возобновлении Chat заменяется доступным fallback.
37
+ _Avoid_: Provider, model settings
38
+
39
+ **Revision**:
40
+ Замена пользовательского запроса, после которой последующие Turn удаляются, Agent Memory, Uploaded Files и служебные артефакты возвращаются к состоянию перед изменяемым Turn, а Agent заново выполняет изменённый запрос.
41
+ _Avoid_: Edit, branch
42
+
43
+ **Local User**:
44
+ Единственный пользователь экземпляра приложения, распознаваемый автоматически без экрана входа.
45
+ _Avoid_: Anonymous user, account
@@ -0,0 +1,34 @@
1
+ # Contributing
2
+
3
+ ## Локальный запуск
4
+
5
+ ```bash
6
+ python -m pip install -e '.[test]' build twine
7
+ localchat init --config-dir . --data-dir .local-agent-chat
8
+ localchat run --config-dir .
9
+ ```
10
+
11
+ Не используйте реальные секреты в тестах. Проверяйте запрет выхода из песочницы на файлах, созданных через `tmp_path`; тест не должен читать настоящие системные или пользовательские файлы.
12
+
13
+ ## Перед PR
14
+
15
+ ```bash
16
+ pytest -q
17
+ ruff check .
18
+ ruff format --check .
19
+ python -m compileall -q local_agent_chat
20
+ bash -n scripts/run.sh
21
+ python -m build
22
+ python -m twine check dist/*
23
+ python scripts/check_distribution.py dist/*.whl
24
+ ```
25
+
26
+ - Добавьте тест для изменённого поведения.
27
+ - Сохраняйте ровно четыре инструмента чтения песочницы: `ls`, `read_file`, `glob`, `grep`. Проверяйте суммаризацию, перезапуск и Revision через реальный `create_agent` с управляемой тестовой моделью.
28
+ - Обновите README или `docs/architecture.md`, если изменился публичный flow.
29
+ - Для нового архитектурного решения добавьте короткий ADR; термины меняйте через `CONTEXT.md`.
30
+ - Не коммитьте `.env`, `models.yaml`, `.local-agent-chat/`, SQLite, скриншоты и логи.
31
+
32
+ Карта файлов и точек настройки есть в README.
33
+
34
+ Проверка дистрибутива создаёт чистое окружение вне checkout, устанавливает только runtime-зависимости и проверяет UI, загрузку файла, вызов инструмента, правку исторического запроса и восстановление после переустановки через локальную тестовую модель. Ресурсы UI находятся в `local_agent_chat/assets/`; CLI копирует их в отдельный временный каталог при запуске.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LocalChat contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ include LICENSE README.md CONTRIBUTING.md SECURITY.md CONTEXT.md
2
+ include .env.example models.example.yaml
3
+ include scripts/run.sh scripts/check_distribution.py
4
+ recursive-include tests *.py
5
+ recursive-include docs *.md
6
+ global-exclude *.pyc
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.4
2
+ Name: local-agent-chat
3
+ Version: 0.1.0
4
+ Summary: Local chat UI with persistent history and a sandboxed ReAct agent
5
+ Author: dev-sergeev
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/dev-sergeev/local-agent-chat
8
+ Project-URL: Documentation, https://github.com/dev-sergeev/local-agent-chat#readme
9
+ Project-URL: Issues, https://github.com/dev-sergeev/local-agent-chat/issues
10
+ Project-URL: Source, https://github.com/dev-sergeev/local-agent-chat
11
+ Keywords: chat,llm,chainlit,react-agent,localchat
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Web Environment
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Requires-Python: <3.14,>=3.12
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: chainlit<2.12,>=2.11
23
+ Requires-Dist: langchain<2,>=1.2
24
+ Requires-Dist: langchain-openai<2,>=1.6
25
+ Requires-Dist: aiosqlite<1,>=0.20
26
+ Requires-Dist: SQLAlchemy<3,>=2.0
27
+ Requires-Dist: PyYAML<7,>=6
28
+ Requires-Dist: python-dotenv<2,>=1
29
+ Requires-Dist: platformdirs<5,>=4
30
+ Provides-Extra: test
31
+ Requires-Dist: pytest<10,>=8; extra == "test"
32
+ Requires-Dist: pytest-asyncio<2,>=1; extra == "test"
33
+ Requires-Dist: python-socketio[client]<6,>=5.11; extra == "test"
34
+ Requires-Dist: requests<3,>=2; extra == "test"
35
+ Requires-Dist: ruff<1,>=0.13; extra == "test"
36
+ Dynamic: license-file
37
+
38
+ # LocalChat
39
+
40
+ Однопользовательский Chainlit UI и обычный ReAct-агент на LangChain: модель вызывает инструменты чтения загруженных файлов и формирует ответ. История диалога, сводка контекста и файлы сохраняются локально.
41
+
42
+ Агент предоставляет ровно четыре инструмента: `ls`, `read_file`, `glob`, `grep`. Их виртуальный `/` — файлы текущего чата. Выход в файловую систему хоста, другие чаты, запись файлов, shell и выполнение кода недоступны. Субагентов, планировщика, файловой выгрузки контекста и общей долговременной памяти нет.
43
+
44
+ Редактирование исторического запроса удаляет его прежний ответ и последующее продолжение. Контекст вместе со сводкой и файлами восстанавливается перед изменяемым запросом, после чего агент отвечает заново. Ошибка или Stop восстанавливают прежнее состояние. Одинаковый текст правки сохраняет весь диалог.
45
+
46
+ ## Установка и первый запуск
47
+
48
+ Требуются Python **3.12–3.13**, Linux (или WSL2) и OpenAI-compatible модель с tool calling. Пакет содержит UI, переводы и все ресурсы приложения; клонировать репозиторий для запуска не нужно. Windows без WSL не поддерживается из-за требований файловой песочницы. macOS пока не входит в проверяемые платформы.
49
+
50
+ После публикации в PyPI установите приложение через [pipx](https://pipx.pypa.io/stable/installation/):
51
+
52
+ ```bash
53
+ pipx install --python python3.12 local-agent-chat
54
+ localchat init
55
+ localchat run
56
+ ```
57
+
58
+ `init` запросит модель, адрес API и ключ. Для OpenAI-compatible endpoint имя модели имеет вид `openai:<model-id>`, например `openai:deepseek/deepseek-v4-flash-0731` для OpenRouter. Ключ вводится без отображения в терминале; секрет сессии создаётся автоматически. `run` покажет адрес UI, по умолчанию **http://127.0.0.1:8765/**. Остановка — `Ctrl+C`; `--open-browser` открывает браузер автоматически.
59
+
60
+ До первого релиза можно установить собранный wheel: `pipx install --python python3.12 ./dist/local_agent_chat-0.1.0-py3-none-any.whl`. Альтернатива pipx — `python3.12 -m venv .venv`, активация окружения и `python -m pip install local-agent-chat`.
61
+
62
+ Для локального API без ключа:
63
+
64
+ ```bash
65
+ localchat init --no-input --model openai:your-model \
66
+ --base-url http://127.0.0.1:8000/v1 --no-api-key
67
+ ```
68
+
69
+ Для автоматической настройки с ключом передайте его в `OPENAI_API_KEY` и используйте те же аргументы без `--no-api-key`. `--api-key-env MY_PROVIDER_KEY` выбирает другое имя переменной; `--no-streaming` отключает потоковый ответ. Полная справка: `localchat init --help`, `localchat run --help`.
70
+
71
+ ## Настройки, данные и обновление
72
+
73
+ На Linux по умолчанию используются:
74
+
75
+ - `~/.config/localchat/.env` — параметры запуска и ключи, права нового файла `0600`.
76
+ - `~/.config/localchat/models.yaml` — профили моделей; ключи здесь не хранятся.
77
+ - `~/.local/share/localchat/` — SQLite, история и вложения.
78
+
79
+ Учитываются `XDG_CONFIG_HOME` и `XDG_DATA_HOME`. `--config-dir PATH` или `LOCALCHAT_CONFIG_DIR` выбирает другой каталог конфигурации, `--data-dir PATH` — каталог данных. Относительные пути из `.env` разрешаются относительно каталога конфигурации. Приоритет: аргументы запуска, затем переменные окружения, затем `.env`, затем значения по умолчанию. `.env` читается как данные: shell-команды и подстановки `${...}` не выполняются.
80
+
81
+ Повторный `init` не перезаписывает настройки. Для смены модели или ключа отредактируйте конфигурацию и перезапустите приложение. Обновление: `pipx upgrade local-agent-chat`; перед обновлением остановите процесс и сделайте резервную копию каталогов конфигурации и данных. Установка и переустановка пакета их не затрагивают. Два процесса не могут одновременно открыть один каталог данных.
82
+
83
+ Если запуск сообщает об отсутствующей конфигурации, выполните `localchat init`. Для занятого порта используйте `localchat run --port 8766`. Ошибки модели отображаются в UI и терминале; используйте endpoint с поддержкой tool calling и бюджетом контекста, подходящим выбранной модели.
84
+
85
+ ## JupyterHub и существующая установка
86
+
87
+ Для прокси задайте **полный публичный префикс**:
88
+
89
+ ```bash
90
+ localchat run --port 8765 \
91
+ --root-path "${JUPYTERHUB_SERVICE_PREFIX%/}/vscode/proxy/8765"
92
+ ```
93
+
94
+ Здесь переменную раскрывает shell перед вызовом команды. В `.env` нужно записать уже готовый путь, например `/user/alice/vscode/proxy/8765`. Неверный префикс может привести к белому экрану из-за неправильных адресов JavaScript. Проверяйте UI по публичному адресу прокси. Прямой порт рассчитан на локальное использование; внешний доступ требует аутентификации прокси, см. [Security](https://github.com/dev-sergeev/local-agent-chat/blob/main/SECURITY.md).
95
+
96
+ Старые `.env`, `models.yaml` и каталог данных можно использовать через `localchat run --config-dir /path/to/checkout`. Сценарий `./scripts/run.sh` сохранён для существующих checkout и по-прежнему обрабатывает `.env` как shell-файл, включая прежние подстановки переменных. В новых установках используйте команды `localchat`. Запуск из исходников и проверки описаны в [Contributing](https://github.com/dev-sergeev/local-agent-chat/blob/main/CONTRIBUTING.md), выпуск — в [инструкции публикации](https://github.com/dev-sergeev/local-agent-chat/blob/main/docs/publishing.md).
97
+
98
+ ## Контекст и лимиты
99
+
100
+ Параметры находятся в `AgentConfig` и могут задаваться через `.env`:
101
+
102
+ | Переменная | По умолчанию | Назначение |
103
+ |---|---:|---|
104
+ | `AGENT_CONTEXT_TOKENS` | 16000 | Бюджет окна с резервом под ответ и инструменты |
105
+ | `AGENT_SUMMARY_TRIGGER_TOKENS` | 10000 | Порог суммаризации сообщений |
106
+ | `AGENT_KEEP_TOKENS` | 3000 | Бюджет последних сообщений, сохраняемых дословно |
107
+ | `AGENT_SUMMARY_TOKENS` | 1000 | Максимальный ответ модели суммаризации |
108
+ | `AGENT_MAX_OUTPUT_TOKENS` | 2000 | Максимальный ответ основной модели |
109
+ | `AGENT_MAX_MODEL_CALLS` | 12 | Максимум обращений основной модели за один запрос |
110
+
111
+ Укажите бюджет не больше реального окна выбранной модели. Счётчик использует консервативную оценку для текста и tool calls, поскольку у совместимых endpoint не всегда доступен точный токенизатор. Конфигурация проверяет запас для ответа, сводки и схем инструментов.
112
+
113
+ Перед обращением к основной модели штатный `SummarizationMiddleware` заменяет старую часть контекста сводкой и сохраняет недавние сообщения, не разрывая tool-call/result пары. Предыдущая сводка включается в следующую. Длинный импортированный контекст суммируется порциями: старые сообщения не отбрасываются из-за стандартного лимита summarizer в 4000 токенов. Сводка не показывается как ответ пользователю и не заменяет полную историю UI.
114
+
115
+ В `summary_options` профиля можно передать параметры модели только для суммаризации. В примере OpenRouter reasoning выключен через `extra_body.reasoning.enabled: false`: иначе небольшой лимит ответа может целиком уйти на reasoning и оставить пустую сводку. См. [параметры reasoning OpenRouter](https://openrouter.ai/docs/guides/best-practices/reasoning-tokens). Для другого провайдера задайте поддерживаемые им параметры. Лимиты ответа и streaming управляются агентом.
116
+
117
+ Пустая или обрезанная сводка, пустой ответ, ошибка модели и превышение бюджета последнего запроса завершают операцию с откатом. Слишком большой запрос следует разделить на части. Вывод одного файлового инструмента ограничен примерно 6000 символами; `read_file` поддерживает постраничное чтение, `grep` ищет буквальную строку.
118
+
119
+ ## Надёжность и хранение
120
+
121
+ Provider SDK повторяет отдельные transient HTTP-запросы; полный ход агента и инструменты не переигрываются. Настройки: `LLM_MAX_RETRIES=3`, `LLM_REQUEST_TIMEOUT_SECONDS=60`, `LLM_STREAM_CHUNK_TIMEOUT_SECONDS=120`, `LLM_STREAM_RETRIES=1`, `LLM_AUXILIARY_TIMEOUT_SECONDS=30`. Дополнительная попытка streaming допустима только до первого полученного chunk. Для суммаризации действует та же политика без вложенного `with_retry`.
122
+
123
+ В `APP_DATA_DIR` находятся:
124
+
125
+ - `chainlit.sqlite3`: сообщения, шаги инструментов, вложения, названия; постоянный `stepOrder` сохраняет порядок даже при одинаковых временных метках.
126
+ - `runtime-history.sqlite3`: только актуальные завершённые запросы/ответы и ссылки на предшествующее состояние.
127
+ - `checkpoints.sqlite3`: выбор модели, текущий контекст и снимки контекста перед запросами. Граф ReAct между вызовами не хранит внутреннее состояние.
128
+ - `sandboxes/`: файлы и снимки файлов; `blobs/`: сохранённые вложения Chainlit.
129
+
130
+ Прежние чаты импортируются из актуальных запросов и ответов. Старые вызовы инструментов и внутренние графы не выполняются. Редактирование старого запроса восстанавливает предшествующую часть видимой истории. Старые режимы доступа исчезают; все чаты получают только чтение своей песочницы. Прежний индекс межчатового поиска удаляется, сохранённый `memory/MEMORY.md` больше не читается агентом. Внутренние старые LangGraph-таблицы могут оставаться в существующей базе как неиспользуемые данные.
131
+
132
+ UI принимает до 20 файлов по 100 MiB; объём активных файлов чата ограничен 1 GiB. Коллизии имён получают суффикс. Удаление чата удаляет его активные данные и снимки нового контекста.
133
+
134
+ ## Где менять код
135
+
136
+ | Задача | Файл |
137
+ |---|---|
138
+ | Сборка агента и цикл выполнения | `local_agent_chat/agent_execution.py` |
139
+ | Суммаризация и бюджет контекста | `local_agent_chat/agent_context.py` |
140
+ | Хранение контекста и откат | `local_agent_chat/agent_memory.py` |
141
+ | Четыре инструмента чтения | `local_agent_chat/sandbox_tools.py` |
142
+ | System prompt и заголовки | `local_agent_chat/prompts.py` |
143
+ | Настройки и provider retry | `local_agent_chat/settings.py`, `local_agent_chat/llm_retry.py` |
144
+ | Координация истории и UI | `local_agent_chat/runtime.py`, `local_agent_chat/chainlit_data.py`, `local_agent_chat/app.py` |
145
+
146
+ [Архитектура](https://github.com/dev-sergeev/local-agent-chat/blob/main/docs/architecture.md), [термины](https://github.com/dev-sergeev/local-agent-chat/blob/main/CONTEXT.md), [ограничения доступа](https://github.com/dev-sergeev/local-agent-chat/blob/main/SECURITY.md), [разработка](https://github.com/dev-sergeev/local-agent-chat/blob/main/CONTRIBUTING.md), [результаты проверок](https://github.com/dev-sergeev/local-agent-chat/blob/main/docs/react-validation.md).
@@ -0,0 +1,109 @@
1
+ # LocalChat
2
+
3
+ Однопользовательский Chainlit UI и обычный ReAct-агент на LangChain: модель вызывает инструменты чтения загруженных файлов и формирует ответ. История диалога, сводка контекста и файлы сохраняются локально.
4
+
5
+ Агент предоставляет ровно четыре инструмента: `ls`, `read_file`, `glob`, `grep`. Их виртуальный `/` — файлы текущего чата. Выход в файловую систему хоста, другие чаты, запись файлов, shell и выполнение кода недоступны. Субагентов, планировщика, файловой выгрузки контекста и общей долговременной памяти нет.
6
+
7
+ Редактирование исторического запроса удаляет его прежний ответ и последующее продолжение. Контекст вместе со сводкой и файлами восстанавливается перед изменяемым запросом, после чего агент отвечает заново. Ошибка или Stop восстанавливают прежнее состояние. Одинаковый текст правки сохраняет весь диалог.
8
+
9
+ ## Установка и первый запуск
10
+
11
+ Требуются Python **3.12–3.13**, Linux (или WSL2) и OpenAI-compatible модель с tool calling. Пакет содержит UI, переводы и все ресурсы приложения; клонировать репозиторий для запуска не нужно. Windows без WSL не поддерживается из-за требований файловой песочницы. macOS пока не входит в проверяемые платформы.
12
+
13
+ После публикации в PyPI установите приложение через [pipx](https://pipx.pypa.io/stable/installation/):
14
+
15
+ ```bash
16
+ pipx install --python python3.12 local-agent-chat
17
+ localchat init
18
+ localchat run
19
+ ```
20
+
21
+ `init` запросит модель, адрес API и ключ. Для OpenAI-compatible endpoint имя модели имеет вид `openai:<model-id>`, например `openai:deepseek/deepseek-v4-flash-0731` для OpenRouter. Ключ вводится без отображения в терминале; секрет сессии создаётся автоматически. `run` покажет адрес UI, по умолчанию **http://127.0.0.1:8765/**. Остановка — `Ctrl+C`; `--open-browser` открывает браузер автоматически.
22
+
23
+ До первого релиза можно установить собранный wheel: `pipx install --python python3.12 ./dist/local_agent_chat-0.1.0-py3-none-any.whl`. Альтернатива pipx — `python3.12 -m venv .venv`, активация окружения и `python -m pip install local-agent-chat`.
24
+
25
+ Для локального API без ключа:
26
+
27
+ ```bash
28
+ localchat init --no-input --model openai:your-model \
29
+ --base-url http://127.0.0.1:8000/v1 --no-api-key
30
+ ```
31
+
32
+ Для автоматической настройки с ключом передайте его в `OPENAI_API_KEY` и используйте те же аргументы без `--no-api-key`. `--api-key-env MY_PROVIDER_KEY` выбирает другое имя переменной; `--no-streaming` отключает потоковый ответ. Полная справка: `localchat init --help`, `localchat run --help`.
33
+
34
+ ## Настройки, данные и обновление
35
+
36
+ На Linux по умолчанию используются:
37
+
38
+ - `~/.config/localchat/.env` — параметры запуска и ключи, права нового файла `0600`.
39
+ - `~/.config/localchat/models.yaml` — профили моделей; ключи здесь не хранятся.
40
+ - `~/.local/share/localchat/` — SQLite, история и вложения.
41
+
42
+ Учитываются `XDG_CONFIG_HOME` и `XDG_DATA_HOME`. `--config-dir PATH` или `LOCALCHAT_CONFIG_DIR` выбирает другой каталог конфигурации, `--data-dir PATH` — каталог данных. Относительные пути из `.env` разрешаются относительно каталога конфигурации. Приоритет: аргументы запуска, затем переменные окружения, затем `.env`, затем значения по умолчанию. `.env` читается как данные: shell-команды и подстановки `${...}` не выполняются.
43
+
44
+ Повторный `init` не перезаписывает настройки. Для смены модели или ключа отредактируйте конфигурацию и перезапустите приложение. Обновление: `pipx upgrade local-agent-chat`; перед обновлением остановите процесс и сделайте резервную копию каталогов конфигурации и данных. Установка и переустановка пакета их не затрагивают. Два процесса не могут одновременно открыть один каталог данных.
45
+
46
+ Если запуск сообщает об отсутствующей конфигурации, выполните `localchat init`. Для занятого порта используйте `localchat run --port 8766`. Ошибки модели отображаются в UI и терминале; используйте endpoint с поддержкой tool calling и бюджетом контекста, подходящим выбранной модели.
47
+
48
+ ## JupyterHub и существующая установка
49
+
50
+ Для прокси задайте **полный публичный префикс**:
51
+
52
+ ```bash
53
+ localchat run --port 8765 \
54
+ --root-path "${JUPYTERHUB_SERVICE_PREFIX%/}/vscode/proxy/8765"
55
+ ```
56
+
57
+ Здесь переменную раскрывает shell перед вызовом команды. В `.env` нужно записать уже готовый путь, например `/user/alice/vscode/proxy/8765`. Неверный префикс может привести к белому экрану из-за неправильных адресов JavaScript. Проверяйте UI по публичному адресу прокси. Прямой порт рассчитан на локальное использование; внешний доступ требует аутентификации прокси, см. [Security](https://github.com/dev-sergeev/local-agent-chat/blob/main/SECURITY.md).
58
+
59
+ Старые `.env`, `models.yaml` и каталог данных можно использовать через `localchat run --config-dir /path/to/checkout`. Сценарий `./scripts/run.sh` сохранён для существующих checkout и по-прежнему обрабатывает `.env` как shell-файл, включая прежние подстановки переменных. В новых установках используйте команды `localchat`. Запуск из исходников и проверки описаны в [Contributing](https://github.com/dev-sergeev/local-agent-chat/blob/main/CONTRIBUTING.md), выпуск — в [инструкции публикации](https://github.com/dev-sergeev/local-agent-chat/blob/main/docs/publishing.md).
60
+
61
+ ## Контекст и лимиты
62
+
63
+ Параметры находятся в `AgentConfig` и могут задаваться через `.env`:
64
+
65
+ | Переменная | По умолчанию | Назначение |
66
+ |---|---:|---|
67
+ | `AGENT_CONTEXT_TOKENS` | 16000 | Бюджет окна с резервом под ответ и инструменты |
68
+ | `AGENT_SUMMARY_TRIGGER_TOKENS` | 10000 | Порог суммаризации сообщений |
69
+ | `AGENT_KEEP_TOKENS` | 3000 | Бюджет последних сообщений, сохраняемых дословно |
70
+ | `AGENT_SUMMARY_TOKENS` | 1000 | Максимальный ответ модели суммаризации |
71
+ | `AGENT_MAX_OUTPUT_TOKENS` | 2000 | Максимальный ответ основной модели |
72
+ | `AGENT_MAX_MODEL_CALLS` | 12 | Максимум обращений основной модели за один запрос |
73
+
74
+ Укажите бюджет не больше реального окна выбранной модели. Счётчик использует консервативную оценку для текста и tool calls, поскольку у совместимых endpoint не всегда доступен точный токенизатор. Конфигурация проверяет запас для ответа, сводки и схем инструментов.
75
+
76
+ Перед обращением к основной модели штатный `SummarizationMiddleware` заменяет старую часть контекста сводкой и сохраняет недавние сообщения, не разрывая tool-call/result пары. Предыдущая сводка включается в следующую. Длинный импортированный контекст суммируется порциями: старые сообщения не отбрасываются из-за стандартного лимита summarizer в 4000 токенов. Сводка не показывается как ответ пользователю и не заменяет полную историю UI.
77
+
78
+ В `summary_options` профиля можно передать параметры модели только для суммаризации. В примере OpenRouter reasoning выключен через `extra_body.reasoning.enabled: false`: иначе небольшой лимит ответа может целиком уйти на reasoning и оставить пустую сводку. См. [параметры reasoning OpenRouter](https://openrouter.ai/docs/guides/best-practices/reasoning-tokens). Для другого провайдера задайте поддерживаемые им параметры. Лимиты ответа и streaming управляются агентом.
79
+
80
+ Пустая или обрезанная сводка, пустой ответ, ошибка модели и превышение бюджета последнего запроса завершают операцию с откатом. Слишком большой запрос следует разделить на части. Вывод одного файлового инструмента ограничен примерно 6000 символами; `read_file` поддерживает постраничное чтение, `grep` ищет буквальную строку.
81
+
82
+ ## Надёжность и хранение
83
+
84
+ Provider SDK повторяет отдельные transient HTTP-запросы; полный ход агента и инструменты не переигрываются. Настройки: `LLM_MAX_RETRIES=3`, `LLM_REQUEST_TIMEOUT_SECONDS=60`, `LLM_STREAM_CHUNK_TIMEOUT_SECONDS=120`, `LLM_STREAM_RETRIES=1`, `LLM_AUXILIARY_TIMEOUT_SECONDS=30`. Дополнительная попытка streaming допустима только до первого полученного chunk. Для суммаризации действует та же политика без вложенного `with_retry`.
85
+
86
+ В `APP_DATA_DIR` находятся:
87
+
88
+ - `chainlit.sqlite3`: сообщения, шаги инструментов, вложения, названия; постоянный `stepOrder` сохраняет порядок даже при одинаковых временных метках.
89
+ - `runtime-history.sqlite3`: только актуальные завершённые запросы/ответы и ссылки на предшествующее состояние.
90
+ - `checkpoints.sqlite3`: выбор модели, текущий контекст и снимки контекста перед запросами. Граф ReAct между вызовами не хранит внутреннее состояние.
91
+ - `sandboxes/`: файлы и снимки файлов; `blobs/`: сохранённые вложения Chainlit.
92
+
93
+ Прежние чаты импортируются из актуальных запросов и ответов. Старые вызовы инструментов и внутренние графы не выполняются. Редактирование старого запроса восстанавливает предшествующую часть видимой истории. Старые режимы доступа исчезают; все чаты получают только чтение своей песочницы. Прежний индекс межчатового поиска удаляется, сохранённый `memory/MEMORY.md` больше не читается агентом. Внутренние старые LangGraph-таблицы могут оставаться в существующей базе как неиспользуемые данные.
94
+
95
+ UI принимает до 20 файлов по 100 MiB; объём активных файлов чата ограничен 1 GiB. Коллизии имён получают суффикс. Удаление чата удаляет его активные данные и снимки нового контекста.
96
+
97
+ ## Где менять код
98
+
99
+ | Задача | Файл |
100
+ |---|---|
101
+ | Сборка агента и цикл выполнения | `local_agent_chat/agent_execution.py` |
102
+ | Суммаризация и бюджет контекста | `local_agent_chat/agent_context.py` |
103
+ | Хранение контекста и откат | `local_agent_chat/agent_memory.py` |
104
+ | Четыре инструмента чтения | `local_agent_chat/sandbox_tools.py` |
105
+ | System prompt и заголовки | `local_agent_chat/prompts.py` |
106
+ | Настройки и provider retry | `local_agent_chat/settings.py`, `local_agent_chat/llm_retry.py` |
107
+ | Координация истории и UI | `local_agent_chat/runtime.py`, `local_agent_chat/chainlit_data.py`, `local_agent_chat/app.py` |
108
+
109
+ [Архитектура](https://github.com/dev-sergeev/local-agent-chat/blob/main/docs/architecture.md), [термины](https://github.com/dev-sergeev/local-agent-chat/blob/main/CONTEXT.md), [ограничения доступа](https://github.com/dev-sergeev/local-agent-chat/blob/main/SECURITY.md), [разработка](https://github.com/dev-sergeev/local-agent-chat/blob/main/CONTRIBUTING.md), [результаты проверок](https://github.com/dev-sergeev/local-agent-chat/blob/main/docs/react-validation.md).
@@ -0,0 +1,13 @@
1
+ # Security
2
+
3
+ Проект рассчитан на одного доверенного пользователя: локально или внутри защищённого JupyterHub. Фиксированный Local User в Chainlit полагается на аутентификацию внешнего прокси; прямой порт не является самостоятельной границей аутентификации.
4
+
5
+ У агента есть только `ls`, `read_file`, `glob`, `grep` для файлов текущего чата. Абсолютные пути трактуются относительно виртуального корня загрузок, а не корня хоста. Компоненты пути открываются относительно дескриптора каталога с `O_NOFOLLOW`; `..`, симлинки и специальные файлы отвергаются. Инструментов записи, исполнения кода, shell, сети, чтения других чатов и Markdown-памяти нет.
6
+
7
+ Файлы и цитируемые сообщения считаются недоверенными данными. Запрет выполнения инструкций из файлов задан в system prompt, а реальная область доступа ограничена самими инструментами. Выбранному model provider передаются запросы, соответствующий контекст и прочитанные фрагменты загруженных файлов. Прикладная песочница не заменяет изоляцию самого процесса контейнером или VM.
8
+
9
+ Сводка сохраняется отдельно от полной истории UI. Ошибка или отмена её построения оставляет прежнее состояние. Устаревшие host-режимы не восстанавливаются из метаданных или checkpoint: старые чаты получают тот же ограниченный набор инструментов.
10
+
11
+ Для сообщения об уязвимости используйте private security advisory репозитория; не публикуйте секреты или пользовательские данные в Issues.
12
+
13
+ CLI слушает `127.0.0.1` по умолчанию. `localchat init` создаёт `.env` и профили с правами `0600`, новые каталоги — `0700`; ключи и пользовательские данные не входят в wheel или sdist. CLI читает `.env` без shell-подстановок. Старый `scripts/run.sh` сохраняет выполнение доверенного shell-файла для совместимости.
@@ -0,0 +1,15 @@
1
+ # Persist model, memory, and sandbox at the chat boundary
2
+
3
+ Each chat selects one model profile and owns separate durable agent memory and a durable sandbox. This keeps resumed chats reproducible, prevents files from leaking between chats, and allows a revision of a user request to roll both memory and files back to the same pre-turn state.
4
+
5
+ ## Consequences
6
+
7
+ Uploaded files live in the chat sandbox rather than SQLite. Deleting a chat deletes its sandbox, and supporting Chainlit's native revision of any user message requires both an agent checkpoint and a sandbox snapshot for every turn.
8
+
9
+ A revision is a backend state transition, not merely a UI update: it truncates the active history after the edited request, restores agent memory and sandbox files to their matching pre-turn state, persists the replacement request, and reruns the agent to produce a new result. These changes must succeed as one coordinated operation; the agent must never continue from a mixture of revised messages, superseded memory, or stale filesystem side effects. Superseded turns remain in a technical audit log but disappear from the active chat timeline.
10
+
11
+ Files attached to a revised request are imported only after its pre-turn Sandbox snapshot has been restored and before the Agent reruns. The import participates in the Revision transaction: provider failure, cancellation, or persistence failure restores the previously active Sandbox and removes tentative revised uploads.
12
+
13
+ The runtime transaction remains open until Chainlit has persisted and committed the replacement UI continuation. It retains the current Agent checkpoint, Sandbox snapshot, and an exact snapshot of every active runtime Turn from the edited Turn onward. If rendering or the Chainlit commit fails after runtime history was replaced, that snapshot compensates the runtime write, restores its FTS sources and audit boundary, and restores Agent Memory and Sandbox before the per-Chat lock is released.
14
+
15
+ LangGraph root graphs do not support using `checkpoint_ns` as an application-level memory branch. Every restore therefore materializes the selected checkpoint as the durable head of a new tracked LangGraph thread; restoring the pre-first-Turn state creates an empty thread. The active thread pointer is persisted before the next Turn, survives restart, and all threads owned by a Chat are removed on deletion.
@@ -0,0 +1,7 @@
1
+ # Start with local persistence behind adapters
2
+
3
+ The application starts with separate local SQLite persistence for Chainlit chat history and LangGraph agent checkpoints, linked by the Chainlit thread identifier. Storage boundaries remain replaceable because SQLite support for the Chainlit data layer is not an officially guaranteed deployment path, while SQLite checkpointing is supported by LangGraph.
4
+
5
+ ## Consequences
6
+
7
+ Resuming a chat must restore both persistence planes without replaying the same history twice. The application will identify one local user silently so Chainlit can expose its built-in chat history without presenting a login screen.
@@ -0,0 +1,13 @@
1
+ # Execute Extended-mode commands in a Chat-specific Python environment
2
+
3
+ > Status: superseded by [ADR 0009](0009-scope-agent-file-reading-by-chat-mode.md).
4
+ > Agent command/code execution and per-Chat Python environments were removed.
5
+ > The text below records the former decision only.
6
+
7
+ Only Extended Agent Mode exposes command execution. Python and shell commands execute through a local Deep Agents backend whose working directory is the Chat's `files/` directory, while absolute paths retain their real host meaning. Every Extended Chat owns a persistent `environment/venv` plus private `HOME`, temp and cache directories. The service environment is removed from `PATH`, user site-packages are disabled and normal `pip` operations target only the Chat venv. Read-only Chat creates no environment.
8
+
9
+ ## Consequences
10
+
11
+ Chat dependencies survive application restarts and remain separate from revisioned files and internal artifacts; editing an earlier Turn rolls those revisioned directories back but does not uninstall packages. Deleting the Chat removes files, artifacts, snapshots, and its environment.
12
+
13
+ This isolates Python dependencies, not arbitrary code. Extended commands and file tools can still access and modify the host filesystem and network with the service user's permissions, and those external changes are not rolled back by Revision. The deployment must provide a dedicated container or VM as the security boundary.
@@ -0,0 +1,15 @@
1
+ # Bind Agent Mode to the Chat at its first Turn
2
+
3
+ Every Chat has an Agent Mode: `chat_files` by default or `host_files`. The UI may change the selection before the first user message. A synchronous Socket.IO pre-dispatch adapter orders Host Files-setting frames and the first valid message frame before Chainlit creates concurrent handler and persistence tasks. The message frame atomically locks the last accepted mode in the authoritative Chat registry; a failed or cancelled Turn does not unlock it. Stop, settings update, and Chat resume also recover the lock from a persisted request created by older or interrupted code. Chainlit metadata mirrors the value but never authorizes capabilities.
4
+
5
+ Both modes expose exactly `ls`, `read_file`, `glob`, and `grep`. Chat Files maps Uploaded Files of the current Chat to virtual `/`; Host Files maps real `/` and therefore accepts process-readable absolute host paths. Explicit replacement Deep Agents filesystem middleware and read-only backends enforce the boundary on the main Agent and general-purpose subagent; the prompt only explains it. Project Skills are routed read-only to both modes and do not expand capabilities.
6
+
7
+ The capability decision and removal of mutation/execution are recorded in [ADR 0009](0009-scope-agent-file-reading-by-chat-mode.md).
8
+
9
+ Legacy `read_only` and `extended` values migrate to `host_files`, preserving the host-read scope those Chats previously had while deliberately removing mutation and execution. Their existing lock flag is preserved; schemas that predate mode selection migrate as locked `host_files`. Unrecognized values fail closed to `chat_files` without escalating their stored lock state.
10
+
11
+ ## Consequences
12
+
13
+ Resume, restart, Revision, and model checkpoints cannot change a Chat's read scope. New modes require an explicit domain and migration change rather than a presentation-only switch. Host Files can disclose process-readable content to the model provider, while Revision restores only Chat-owned files and agent artifacts; the deployment container or VM remains the security boundary.
14
+
15
+ Chainlit exposes no synchronous pre-message callback, so the adapter isolates one private `python-socketio` seam and verifies its expected `_handle_event` signature at startup. A dependency update that changes this seam fails explicitly rather than silently weakening the invariant.
@@ -0,0 +1,9 @@
1
+ # Retrieve Global Memory from canonical active Turns on demand
2
+
3
+ Global Memory is implemented as two read-only Agent tools: bounded search across other Chats and bounded reading of a selected result with nearby Turns. The tools query an SQLite FTS5 index derived transactionally from canonical active `turns`. They never search the current Chat or `superseded_turns`, and retrieved text is marked as untrusted historical data rather than instructions.
4
+
5
+ The complete Chat History is not added to every model request. Embeddings and automatic extraction by a second model call remain deferred; FTS5 is local, deterministic, and effective for paths, identifiers, commands, errors, and quoted prior decisions. ADR 0008 separately introduces a small curated Markdown snapshot that is always loaded, without adding the full history.
6
+
7
+ ## Consequences
8
+
9
+ Append, answer update, Revision, and Chat deletion update the index in the same SQLite transaction as canonical history, so stale branches disappear immediately. The Agent decides when prior context is relevant and first sees short candidates before reading a source. Semantic paraphrases may have lower recall than a future hybrid vector index, but no external embedding provider receives the history and the index can be rebuilt from active Turns.
@@ -0,0 +1,13 @@
1
+ # Retry LLM calls at the provider boundary
2
+
3
+ Every model created from a Model Profile uses the provider SDK's native retry at the boundary of one HTTP inference. The provider classifies transient connection failures, timeouts, rate limits and retryable HTTP statuses, applies exponential backoff with jitter, and honors `Retry-After`; the environment controls provider attempts, safe stream resumes, and request, stream-chunk, and auxiliary-call timeouts.
4
+
5
+ Deep Agents' summarization middleware normally adds a second broad retry loop. The application replaces that inner runnable with the same provider-configured model, so `LLM_MAX_RETRIES` remains authoritative for each inference and provider retry loops cannot nest.
6
+
7
+ Deep Agents exposes no public option for disabling only this inner loop. The adapter therefore verifies the supported middleware shape and fails at graph construction if it changes; dependency upgrades must keep the integration test green instead of silently restoring nested retries.
8
+
9
+ ## Consequences
10
+
11
+ Provider retry stops once a stream has produced a chunk, because repeating a partially visible response would duplicate output. `LLM_STREAM_RETRIES` gives zero-chunk stream timeouts a separate finite budget around only the innermost model handler. It sits inside Deep Agents' summarization wrapper, so context offload, the Agent graph, Turn and tools are not replayed. Each new inference still has the per-inference provider budget. Permanent request, authentication, and validation errors fail immediately.
12
+
13
+ Deep Agents' existing context-overflow recovery is a separate semantic operation: it may compact the Agent Memory context and issue a new inference. It is not a transport retry of the rejected HTTP inference and does not replay a completed tool call.
@@ -0,0 +1,7 @@
1
+ # Load Project Skills from versioned packages
2
+
3
+ Project Skills live in `skills/<name>/SKILL.md` packages and are loaded through Deep Agents' native Skills source rather than being stored in Chat History, Uploaded Files or model configuration. This keeps procedural instructions reviewable with the application, supports progressive disclosure and gives the main Agent and its standard subagent one canonical source. Chat Files receives an explicit read-only route to this trusted source; Host Files can read the same absolute source directly.
4
+
5
+ ## Consequences
6
+
7
+ Skills are trusted project instructions, shared by all Chats and available in both Agent Modes. They cannot expand a mode's filesystem scope, add tools, or make reference scripts executable. Their metadata enters Agent Memory on the first Turn, so a new or changed Skill becomes visible in a new Chat rather than mutating the behavior of an existing Chat midstream.
@@ -0,0 +1,7 @@
1
+ # Keep curated Long-term Memory in bounded Markdown
2
+
3
+ Long-term Memory is a small current snapshot in `APP_DATA_DIR/memory/MEMORY.md`, loaded through Deep Agents' `MemoryMiddleware` and updated by narrow `remember_context` and `forget_context` tools. This complements rather than replaces FTS history retrieval: the main Agent semantically decides what is durable during its existing inference, while stable keys, atomic upsert, strict limits and credential filtering keep the shared prompt context compact and reviewable without another extraction model or embeddings provider.
4
+
5
+ ## Consequences
6
+
7
+ The middleware refreshes the file before every Turn so already-open Chats see changes. The general-purpose subagent receives a reference snapshot without the narrow mutation tools; those tools remain on the main Agent in both Agent Modes and do not grant generic filesystem writes. Host Files may read the managed Markdown by absolute path when the process permits it, but neither mode has a generic tool that can change it. Parallel updates are serialized across Agent instances before atomic replacement. Once accepted, a mutation reaches a durable decision even if the Turn is cancelled; a published change is an independent commit that survives a later Turn failure or cancellation, Revision, and deletion of the originating Chat. Correction or removal uses the same key or `forget_context`. The main model provider receives the bounded snapshot on every Turn; recognizable credentials are rejected or filtered, but users must still avoid placing secrets in the file. Memory remains untrusted prompt data: an Agent must not persist instructions copied from files or tool output, but this model-level rule is not a hard prompt-injection boundary.
@@ -0,0 +1,35 @@
1
+ # Scope Agent file reading by Chat Mode and remove mutation/execution
2
+
3
+ Every new Chat starts in `chat_files`. Its file tools see Uploaded Files of that
4
+ Chat under virtual `/`, plus an explicit read-only route for trusted Project
5
+ Skills. A separate private route serves Deep Agents' internal artifacts. Before
6
+ the first user message, the user may select `host_files`, whose default read
7
+ scope is real `/` and therefore supports absolute host paths available to the
8
+ application process. The selected mode is immutable after the first Turn.
9
+
10
+ Both modes expose exactly `ls`, `read_file`, `glob`, and `grep`. Explicit
11
+ replacement `FilesystemMiddleware` instances give the main Agent and standard
12
+ general-purpose subagent that same interface. Their backends reject file
13
+ mutation even if an old checkpoint attempts a stale tool call. The application
14
+ does not register `write_file`, `edit_file`, `delete`, or `execute`, and does not
15
+ construct an Agent shell/code backend or a per-Chat Python environment. Project
16
+ Skills remain available in both modes as instructions; `allowed-tools` metadata
17
+ and reference scripts cannot expand these capabilities.
18
+
19
+ The writable artifacts route is an internal middleware interface for bounded
20
+ context offload, not an Agent-visible mutation capability. The narrow
21
+ `remember_context` and `forget_context` tools likewise update only managed
22
+ Long-term Memory and are not general filesystem tools.
23
+
24
+ Legacy `read_only` and `extended` values migrate to `host_files` while retaining
25
+ their stored lock state. This preserves their former ability to read host paths
26
+ while intentionally dropping all mutation and execution. Schemas that predate
27
+ mode selection migrate as locked; unknown values fail closed to `chat_files`.
28
+
29
+ ## Consequences
30
+
31
+ Uploaded-file analysis works without disclosing unrelated host files by
32
+ default. Host Files remains an explicit confidentiality-sensitive choice:
33
+ process-readable content can be sent to the model provider. Revision can fully
34
+ restore Chat-owned files and artifacts because Agent tools cannot change
35
+ external host paths. ADR 0003's command-environment design is superseded.
@@ -0,0 +1,7 @@
1
+ # Coordinate the native editor with the current Chat History
2
+
3
+ Chainlit's default editor schedules message updates and deletions independently, while its Message and Step APIs return before SQLite persistence finishes. LocalChat keeps the native UI but handles `edit_message` inside the Chat lock, stages recovery before truncation, waits for background writes before committing, and republishes the authoritative SQLite timeline on both success and rollback. Confirming identical text is a no-op, and the edit task participates in Stop cancellation.
4
+
5
+ Only current Turn history is retained; this supersedes ADR 0001's permanent technical audit requirement. Temporary recovery rows protect an in-flight Revision and are discarded after its outcome is known; existing `superseded_turns` data is removed on startup without changing active Turns.
6
+
7
+ Deep Agents' message state uses delta checkpoints, so copying a single raw checkpoint loses earlier messages. Restore materializes delta channels through the graph's state API before publishing a new memory thread. This retains the existing checkpoint-token format and restores old Chats without copying obsolete transcript branches. Regression coverage uses the real Deep Agents graph and SQLite, since a graph with ordinary full-state channels does not reproduce the failure. See the [LangGraph delta-channel contract](https://reference.langchain.com/python/langgraph/channels/delta).
@@ -0,0 +1,5 @@
1
+ # Use one sandboxed ReAct loop with explicit persisted context
2
+
3
+ Replace Deep Agents with LangChain `create_agent`, four read-only Sandbox tools and an explicit middleware list. Persist only messages and rolling summaries between Turns, keeping the graph ephemeral: Revision then restores a small application-owned snapshot instead of reconstructing internal checkpoint ancestry. This trades resuming a partially completed graph for simpler, predictable Turn rollback and context migration.
4
+
5
+ This decision supersedes ADRs 0003–0005 and 0007–0009's agent capabilities, modes, subagents, skills and shared memory. ADR 0006's provider retry boundary and ADR 0010's coordinated UI commit remain. Existing Chats retain their visible history; legacy context is imported from completed requests and answers, never by executing obsolete tools or graph state. Every Chat now reads only its uploaded files, including Chats previously configured for host access.
@@ -0,0 +1,3 @@
1
+ # Ship immutable UI resources and keep user data outside the installation
2
+
3
+ LocalChat is installed as a wheel with a Python CLI and all UI assets; configuration and durable Chat data live in explicit user directories. Chainlit requires a writable application root for temporary uploads, so a small CLI parent copies the shipped UI into a disposable workspace, starts Chainlit in a child process, forwards stop signals and holds a lock on the persistent data directory. Chainlit force-exits during shutdown, so the parent owns workspace cleanup. This costs a small asset copy at startup but keeps package upgrades independent of user data, avoids writes to site-packages, and prevents competing processes from corrupting cross-database Revision transactions.