- TypeScript 98.5%
- Nix 1.4%
- JavaScript 0.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| bin | ||
| nix | ||
| src | ||
| test | ||
| .gitignore | ||
| config.example.json | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
Personal Assistant
Персональный ИИ-секретарь с одним Git workspace. Сервер — единственный владелец сохраняемой Pi-сессии, модели, workspace и напоминаний. Межпроцессный lock не позволяет двум экземплярам сервера или initializer одновременно изменять один workspace. CLI и Matrix bridge — HTTP-клиенты.
Этот README — единственное каноническое описание проекта вне исходного кода.
Архитектурные принципы
Файлы — источник истины
Долговременные пользовательские данные хранятся в отдельном Git workspace как Markdown (.md) и Org (.org). Код не вводит обязательные классы Task, Project или CalendarEvent: структуру проектов, задач и заметок определяют текст, существующее дерево файлов и инструкции агента.
Код моделирует capabilities, lifecycle и security boundaries, а не пользовательскую предметную область.
Git обеспечивает историю, diff, откат, синхронизацию и резервное копирование. Pi остаётся внутренней implementation detail, а клиенты — тонкими интерфейсами к одному HTTP-сервису.
Поток запроса
CLI / Matrix
→ HTTP Secretary Service
→ WorkspaceCoordinator
→ интерактивная Pi AgentSession
→ sandboxed filesystem / notification / fetch / autonomous tools
→ Git commit или rollback
→ .secretary/runtime.json и HTTP response
Основные компоненты:
src/secretary.ts— composition root, интерактивные и автономные ходы;src/server.ts— HTTP API;src/workspace-coordinator.ts— внутрипроцессная очередь с приоритетом пользователя;src/workspace-fs.ts— изолированные файловые операции;src/git-workspace.ts— Git lifecycle;src/runtime-state.ts— служебное persisted state;src/autonomous.ts,src/autonomous-state.ts— scheduler, квоты и recovery;src/matrix.ts— Matrix bridge;src/workspace-lock.ts— межпроцессное владение workspace.
Threat model и KISS
Проект рассчитан на trusted single-user окружение: пользователь, процесс сервера и установленные Pi extensions доверенные, а вызовы tools со стороны модели и сетевое содержимое — нет. Файловые tools не выходят из workspace, сеть доступна только через allowlist, сырой shell модели не предоставляется. Инструмент move_file перемещает и переименовывает файлы и каталоги внутри workspace через rename, не читая содержимого; существующий файл назначения не перезаписывается.
Преднамеренные локальные TOCTOU-атаки, изоляция доверенных extensions и полноценный OS sandbox пока не входят в модель защиты. Без подтверждённой практической необходимости проект не добавляет отдельную БД, парсеры Markdown/Org, сложный workflow engine, unrestricted shell или неограниченные autonomous loops.
Запуск
npm install
cp config.example.json config.json
npm run init-workspace -- config.json
SECRETARY_API_KEY='…' npm run serve -- config.json
workspaceRoot можно указать абсолютным путём или относительным путём от каталога конфигурационного файла. Если его не указать, workspace — каталог, содержащий конфиг; конфиг разрешено хранить в самом workspace. Workspace не должен пересекаться с репозиторием сервиса. Ключ модели можно задать как model.apiKey в JSON или через SECRETARY_API_KEY; переменная окружения имеет приоритет. Опциональный SECRETARY_BEARER_TOKEN защищает все API-запросы. SECRETARY_LOG_TOOL_CALLS=1 выводит в stderr имя, ID, аргументы и результат каждого вызова tool. Журнал может содержать приватные данные и секреты. По умолчанию сервер слушает 127.0.0.1:3000.
Пример конфига включает webFetch.allowedOriginsFile — файл allowlist для fetch_url и fetch_feed (относительный путь — от workspaceRoot) должен существовать и быть читаемым до старта сервера, иначе запуск завершится ошибкой. Inline-список allowedOrigins можно оставить пустым, задать в нём постоянные origins или удалить вместе с allowedOriginsFile. Если fetch не нужен, удалите блок webFetch целиком.
В отдельном терминале запускаются тонкие клиенты:
npm start -- --config config.json
MATRIX_ACCESS_TOKEN='…' npm run matrix -- config.json
CLI и Matrix bridge используют workspaceRoot только для локального служебного состояния; им не нужны ключ модели и её настройки.
Конфигурация
workspaceRoot— путь к Git workspace с пользовательскими данными. Абсолютный путь используется как есть, относительный разрешается от каталога config-файла. По умолчанию — каталог config-файла. Не может пересекаться с репозиторием сервиса; config-файл может находиться внутри workspace.assistantDirectory— относительный путь к каталогу автоматически загружаемого долгосрочного контекста (assistantпо умолчанию). Рекурсивно загружаются все его несокрытые Markdown- и Org-файлы без специальных исключений.memoryPath— относительный путь к файлу фактов и предпочтений; по умолчанию<assistantDirectory>/memory.md.agendaPath— относительный путь к индексу датированных задач; по умолчанию<assistantDirectory>/agenda.md.autonomous.memoryPath— относительный путь к автономной рабочей памяти (autonomous/memory.mdпо умолчанию). Она отдельно загружается в автономных сессиях и подчиняетсяautonomous.maxMemoryBytes.piResourcesRoot— необязательный путь к существующему каталогу ресурсов Pi, принадлежащему владельцу сервиса. Абсолютный путь используется как есть, относительный разрешается от каталога config-файла. По умолчанию используется<workspaceRoot>/.pi; если этого каталога нет, ресурсы Pi не загружаются. Он не должен пересекаться с репозиторием сервиса.timezone— IANA timezone для дат и времени, возвращаемых toolget_current_time, и для локального времени ежедневных автономных заданий; по умолчанию timezone процесса.model— блок всех параметров модели:apiBaseUrl— HTTP(S) endpoint OpenAI-совместимого API модели.id— идентификатор модели у этого endpoint.apiKey— ключ API модели; пустая строка и отсутствие поля равнозначны, предпочтительнее задавать черезSECRETARY_API_KEY, который имеет приоритет.reasoning— поддерживает ли модель расширенное рассуждение (reasoning).supportsReasoningEffort— принимает ли endpoint параметрreasoning_effort; по умолчанию равенreasoning. Модель может уметь reasoning, но не принимать effort — но не наоборот:trueтребуетreasoning: true. Приfalseуровень thinking в запросе не участвует, модель рассуждает своими средствами.thinkingLevel— уровень расширенного рассуждения:off(по умолчанию),minimal,low,medium,high,xhighилиmax. Уровень вышеoffтребуетreasoning: true. Уровень, отключённый вthinkingLevelMap(значениеnull), а такжеxhigh/maxбез явной записи в карте — ошибка конфигурации, а не молчаливое понижение.thinkingLevelMap— необязательный маппинг уровней в значения провайдера (reasoning_effort);nullотключает уровень,xhigh/maxдоступны только с явной записью. Пример для Qwen3.8:{ "off": "none", "minimal": null, "low": "low", "medium": "medium", "high": null, "xhigh": "xhigh", "max": null }. Маппинг"off": "none"приreasoning: trueявно выключает thinking запросомreasoning_effort: "none".contextWindow— размер контекстного окна модели в токенах.maxTokens— верхняя граница токенов ответа (max_tokens), 4096 по умолчанию. У многих OpenAI-совместимых серверов reasoning-токены считаются в этом бюджете — при включённом reasoning лимит стоит увеличить.supportsImages— может ли модель принимать изображения во входе.
serverHost,serverPort— интерфейс и порт, на которых слушает сам Secretary (127.0.0.1:3000по умолчанию).secretaryApiUrl— публичный URL Secretary для CLI и Matrix bridge. По умолчанию составляется изserverHostиserverPort; задаётся отдельно при reverse proxy.terminalTurnRecordLimit— положительный лимит сохранённых завершённых идемпотентных запросов.webFetch.allowedOrigins— точные HTTP(S) origins, которымfetch_urlиfetch_feedмогут передаватьGETв интерактивных и автономных сессиях. Пустой список или отсутствие блока отключает tool;"*"разрешает любые HTTP(S) origins.webFetch.allowedOriginsFile— путь к файлу с origins в дополнение кallowedOrigins; обе формы можно задать одновременно. Относительный путь разрешается отworkspaceRoot(удобно для скрытых каталогов workspace — например,.secretary/fetch-origins.txt), абсолютный используется как есть. Одна строка — один origin; пустые строки и#-комментарии игнорируются. Действующие origins — объединение inline-списка и файла;"*"в любом из них разрешает всё. Оба проверяются при старте; файл перечитывается при каждом вызовеfetch_url, поэтому правки применяются без рестарта сервера.autonomous— необязательный server-only блок настроек отложенных самосообщений модели (см. «Автономные самосообщения»). По умолчанию выключен.response.suppressToolNarration— не просить модель описывать промежуточное использование tools.response.finalMessageOnly— возвращать клиенту только последнее сообщение assistant в turn.response.showDiff— добавлятьdiffк успешному chat-ответу.matrixсодержитhomeserver,userId,accessToken,roomId, необязательныйsecretaryApiUrl,markdown(булево, по умолчаниюtrue) иmarkdownLineBreaks(булево, по умолчаниюfalse). Приmarkdown: trueответы модели преобразуются из Markdown в безопасный Matrix HTML (formatted_bodyс исходным Markdown в fallbackbody);markdownLineBreaks: trueпревращает одиночные переводы строк в HTML<br>. Приmarkdown: falseсообщения отправляются обычным текстом безformat.MATRIX_ACCESS_TOKENпереопределяетmatrix.accessToken. Входящие сообщения с вложениями (m.file,m.image,m.audio,m.video) мост скачивает через авторизованный media endpoint homeserver (незашифрованные события сmxc://-адресом, до 7 MiB — лимит увязан с 10 MiB JSON-лимитомPOST /filesс учётом base64), загружает на сервер черезPOST /files, после чего файл лежит вattachments/и зафиксирован в Git, и передаёт агенту сообщение с именем, размером, MIME-типом, путёмattachments/<id>-<имя>в workspace и подписью пользователя как его сообщением. Зашифрованные комнаты и вложения не поддерживаются. Ошибки скачивания или загрузки мост отправляет в комнату текстомRequest failed: …без обращения к агенту; сбой моста между загрузкой вложения и фиксацией события может при повторной обработке создать вattachments/дубликат файла. Внутри workspace агент перемещает бинарные вложения инструментомmove_file, не читая содержимое.
Пути layout задаются относительно workspaceRoot, не могут содержать hidden/parent-компоненты или symlink и должны обозначать разные файлы. При изменении путей сервис не перемещает старые данные и не переписывает существующий AGENTS.md: миграцию и Git-коммит владелец workspace выполняет вручную до перезапуска. Если workspace уже инициализирован (AGENTS.md существует), initializer откажется запускаться при отсутствии любого configured path вместо создания и коммита пустых замен.
Переменные окружения
| Переменная | Назначение |
|---|---|
SECRETARY_API_KEY |
Ключ API модели для сервера. Имеет приоритет над model.apiKey из конфигурации. |
SECRETARY_BEARER_TOKEN |
Токен защиты HTTP API. При задании клиенты CLI и Matrix передают его в Authorization: Bearer …; сервер требует этот заголовок для всех endpoints. |
MATRIX_ACCESS_TOKEN |
Токен Matrix-клиента. Имеет приоритет над matrix.accessToken в конфигурации. |
SECRETARY_LOG_TOOL_CALLS=1 |
Включает запись в stderr имени, ID, аргументов и результата каждого tool-вызова. Лог может содержать приватные данные и секреты. |
RIPGREP_CONFIG_PATH |
Служебная переменная: при запуске сервис устанавливает её сам на файл .secretary/.ripgreprc workspace, чтобы grep не видел скрытые файлы. Вручную задавать её не требуется. |
В piResourcesRoot сервис автоматически загружает только существующие подкаталоги extensions/ и skills/; их создавать не нужно. В extensions/ допустимы обычные Pi entry points (name.ts, name.js, name/index.ts или name/index.js) и Pi packages: пакет помещается непосредственным дочерним каталогом extensions/ и объявляет entry points в package.json через pi.extensions и pi.skills. Его зависимости должны быть установлены в самом пакете. Skills имеют вид skills/<name>/SKILL.md: в prompt попадают только имя и описание, а полный текст агент получает инструментом load_skill. Вспомогательные файлы skills не получают доступа вне workspace. Обычный discovery ресурсов workspace (.agents/, package-файлы и другие пути) отключён: загружается только явно заданный piResourcesRoot, в том числе когда это <workspaceRoot>/.pi.
Extensions — доверенный код владельца сервиса с полными правами процесса, поэтому piResourcesRoot не является защитной границей от вредоносного extension (в MVP также не проверяется дерево symlink extensions). Их зарегистрированные tools и обычные agent/tool hooks доступны автоматически. Extension commands, UI API, session_start и resources_discover не поддерживаются. Изменения ресурсов владельцем вступают в силу при reload на следующем turn.
Получение страниц и файлов
Tools fetch_url и fetch_feed включаются непустым webFetch.allowedOrigins и доступны как в обычном диалоге, так и в автономных сессиях; в автономной сессии правило о недоверенных данных действует так же, как в интерактивной. fetch_url без path возвращает модели текстовый HTTP(S)-ответ размером до 256 KiB. С относительным path он сохраняет до 10 MiB сырых байтов в workspace, создаёт родительские каталоги и не перезаписывает существующий файл. fetch_feed принимает RSS 2.0 и Atom, сортирует элементы по убыванию даты и возвращает не более 20 элементов по умолчанию (до 100); since включает элементы с указанной даты включительно, а тело каждого элемента очищается от HTML и ограничено 4 096 символами по умолчанию (до 16 384). Оба инструмента используют только GET, не более пяти redirect и общий timeout 30 секунд; каждый redirect снова проверяется по allowlist. Бинарный ответ fetch_url без path отклоняется. Загруженный файл участвует в обычном Git lifecycle успешного turn.
Значения allowlist имеют вид https://example.org и включают protocol, hostname и port. "*" разрешает все HTTP(S) origins, в том числе localhost, локальную сеть и metadata endpoints, и тем самым открывает модели широкий сетевой и потенциальный exfiltration-канал. Используйте wildcard только осознанно либо ограничивайте исходящий трафик сервиса системным firewall/proxy. Tool не передаёт пользовательские headers, cookies, credentials или request body; промпт в обеих модах требует относиться к полученному содержимому как к недоверенным данным, а не инструкциям.
Allowlist можно держать и в файле: webFetch.allowedOriginsFile комбинируется с inline-списком, действующие origins — их объединение (wildcard в любом источнике разрешает всё). Храните файл в скрытом (начинающемся с точки) каталоге — например, в workspace рядом с .secretary/runtime.json: агент не может читать и изменять dot-файлы, поэтому не сможет расширить собственный allowlist. Tool перечитывает файл при каждом вызове, а изменения, нарушающие валидность (недоступный файл или невалидная строка), отклоняют конкретный вызов, а не весь сервер. Файл читается процессом сервиса напрямую, поэтому он должен быть доступен на чтение его пользователю.
Интерактивная история Pi сохраняется как нативная JSONL-сессия в <workspaceRoot>/.secretary/sessions/ и продолжается после рестарта сервера. Эти файлы находятся вне Git и недоступны файловым tools агента, но содержат полный приватный transcript, tool calls и результаты; права на .secretary и резервные копии должны ограничиваться пользователем сервиса. Pi автоматически компактизирует длинную историю. Команда /new создаёт новую сохраняемую сессию и отбрасывает ожидающий runtime inbox, не удаляя старые JSONL-файлы. Команда /commit фиксирует все незакоммиченные изменения workspace, включая новые файлы; если изменений нет, она ничего не делает. Команда /usage возвращает оценку занятого и свободного context window в токенах и процентах. Команда /compact [инструкции] вручную сворачивает историю диалога; необязательные инструкции передаются модели для формирования summary. Сразу после compaction оценка недоступна до следующего ответа модели. Долговременный контекст автоматически загружается только из assistantDirectory; расположенные вне него memoryPath и agendaPath модель читает обычными файловыми tools по необходимости.
HTTP API
Если задан SECRETARY_BEARER_TOKEN, каждый endpoint требует Authorization: Bearer <token>.
POST /chat: тело{ "message": "..." }, обязательный заголовокIdempotency-Key(1–200 печатных ASCII-символов без пробелов). Ответ:{ "text", "commit" }, а приshowDiffтакжеdiff.POST /files:{ "name", "contentBase64" }создаёт вложение в workspace.GET /files/:idскачивает вложение.GET /events?after=<id>возвращает упорядоченные notifications и автономные события.GET /autonomous— состояние автономных заданий: включение, пауза, блокировки, остатки бюджетов, активные задания и ограниченная история завершённых заданий (включая неуспешные, с кодом и кратким описанием ошибки). Ошибки заданий также пишутся в журнал сервера.POST /autonomous/pause— сохраняемая общая пауза исполнения; не прерывает уже идущий ход.POST /autonomous/resume— снятие паузы и блокировок; при «грязном» workspace возвращает 409.POST /autonomous/jobs/:id/cancel— идемпотентная отмена ещё не запущенного задания (повторная отмена уже отменённого безопасна); для running и завершённых (completed/failed/interrupted) — 409 с фактическим статусом.
Повтор POST /chat с тем же ключом и тем же сообщением возвращает сохранённый HTTP-результат. Тот же ключ с иным сообщением возвращает 409.
Runtime state и уведомления
Служебное состояние хранится отдельно от пользовательских данных в <workspaceRoot>/.secretary/: runtime.json содержит идемпотентные turn records, напоминания, автономные задания и журнал событий; sessions/ — нативные интерактивные Pi-сессии; CLI и Matrix bridge сохраняют там свои cursors. Запись runtime выполняется atomic replace. workspace.lock — advisory lock, удерживаемый сервером через flock; второй сервер завершается сразу с ошибкой. Каталог добавляется в .gitignore при инициализации workspace. Агенту недоступны все файлы и каталоги workspace, чьи имена начинаются с точки.
Инструменты schedule_notification, list_notifications, cancel_notification работают с общим набором напоминаний. При срабатывании появляется event; CLI выводит накопленные события только до и после пользовательского запроса, Matrix bridge опрашивает их в своём последовательном sync loop. Перед следующим обычным интерактивным ходом Secretary добавляет в контекст свежий хвост ещё не переданных модели событий (уведомлений и публикаций автономных сессий), затем помечает весь этот inbox переданным независимо от успеха модели. /new отбрасывает ожидающий inbox; журнал /events при этом не меняется.
Автономные самосообщения
При включённом autonomous.enabled модель может планировать отложенные запуски самой себя. Это отдельный механизм от уведомлений: уведомление доставляет готовый текст пользователю, самосообщение запускает новый ход модели.
При включённом autonomous.enabled в интерактивном ходе также доступен run_async_task с формой { task }: он создаёт обычное одноразовое задание на немедленный запуск после текущего успешного хода и сразу возвращает его ID. Задача выполняется в отдельной автономной сессии и должна передать пользователю краткий итог через publish_user_message; до этого интерактивный ход не считает её завершённой.
Остальные доступные инструменты: schedule_self_message, list_self_messages, cancel_self_message; в автономной сессии также publish_user_message — явная публикация одного сообщения пользователю. schedule_self_message принимает обычную одноразовую форму { at, message } и, только в интерактивном ходе, календарную форму { at, message, repeat: "daily", until?, maxRuns? }. Для ежедневной формы обязателен хотя бы один из лимитов until и maxRuns; локальное время первого at интерпретируется в настроенной timezone. Автономный ход может создавать только одноразовых потомков. Инструменты уведомлений (schedule_notification, cancel_notification, list_notifications) намеренно доступны только в обычном диалоге: в автономной сессии их нет, чтобы доставку пользователю нельзя было обойти мимо явной публикации. Настроенные fetch_url и fetch_feed, напротив, доступны и в автономных сессиях: allowlist webFetch.allowedOrigins — единая политика доступа для обеих мод, а автономный промпт получает то же правило о недоверенных данных. Принятое задание сначала получает статус staged и активируется только после успешного завершения создающего хода. «Немедленный» run_async_task всё равно может стартовать позже из-за существующих ограничений очереди, в частности minRunIntervalSeconds и лимита запусков за 24 часа. Финальный текст модели сам по себе пользователю не отправляется.
Каждое задание исполняется в новой изолированной Pi-сессии без истории диалога. Преемственность обеспечивается файлом autonomous.memoryPath: он явно загружается в автономные сессии с лимитом autonomous.maxMemoryBytes (при превышении — явная ошибка без обрезания). В интерактивном диалоге он загружается автоматически только тогда, когда является Markdown/Org-файлом внутри assistantDirectory; иначе он остаётся доступен обычным файловым tools. Пути memoryPath и agendaPath также автоматически загружаются только при нахождении внутри assistantDirectory.
Ограничения назначает и хранит сервер в .secretary/runtime.json, они переживают рестарт: maxPending, maxCreatedPerTurn, minDelaySeconds, minRunIntervalSeconds, maxRunsPer24Hours (скользящее окно 24 часов), maxChainDepth, maxJobsPerChain (кумулятивный бюджет цепочки, не возвращается отменами и ошибками), maxRunSeconds и maxModelStepsPerRun (ограничения одного хода), maxMessageBytes, terminalJobRecordLimit. Потомки создаются только от успешного хода; автономный ход не может создавать новые корни цепочки.
Ограничение остановки хода: maxRunSeconds и maxModelStepsPerRun реализованы через кооперативный abort Pi. При срабатывании лимита журнал и terminal history получают соответственно max_run_seconds или max_model_steps, а не общий aborted. Все встроенные инструменты завершаются по сигналу abort, но доверенное extension, игнорирующее сигнал и зависшее в середине операции, может задержать ход дольше лимита и держать очередь workspace занятой — сервер не завершает такой ход принудительно. Держите TimeoutStopSec службы с запасом выше maxRunSeconds (см. Nix-документацию).
Ежедневное задание остаётся одной job с неизменными ID, root и chain. Первый запуск задаёт at, следующие сохраняют то же локальное время в настроенной IANA timezone, поэтому переход DST не сдвигает пользовательское время. Для несуществующего времени весеннего перехода применяется Temporal compatible: оно сдвигается вперёд на величину разрыва; при осеннем повторе выбирается первое вхождение. На следующий обычный день серия возвращается к исходному локальному времени. maxRuns считает начатые (claimed) запуски, включая failed и interrupted. until — абсолютный последний допустимый момент начала (now <= until). После простоя выполняется не более одного catch-up; пропущенные дни не материализуются. Ошибка отдельного запуска не повторяется, но серия продолжается, пока остаётся лимит. Отдельная история occurrences не хранится; активное состояние и итог серии видны через существующие list/control API.
Управление без модели — через HTTP API (/autonomous). Пауза и отмена не прерывают уже идущий ход. Если при старте задания workspace «грязный», ручные изменения сохраняются, задание остаётся ожидать, а исполнитель блокируется до POST /autonomous/resume после исправления. После рестарта прерванный одноразовый ход помечается interrupted и автоматически не повторяется; прерванный запуск периодической job расходует один maxRuns и серия продолжается со следующего будущего интервала, если лимиты не исчерпаны. staged-потомки прерванного хода отменяются.
Опубликованные сообщения появляются в /events с kind: "agent"; Matrix доставляет их как обычные события, CLI показывает накопленные события при следующем взаимодействии. Сбой между Git-коммитом и записью runtime не повторяется автоматически: такой запуск считается прерванным с неопределённым результатом, решение принимает пользователь.
Git lifecycle
Перед turn workspace должен быть чистым. Успешные изменения коммитятся как Assistant update. Ошибочный agent turn очищается до HEAD командой git reset --hard HEAD и git clean -fd; игнорируемые пользовательские файлы не удаляются. Сервер захватывает fail-fast advisory lock <workspaceRoot>/.secretary/workspace.lock до чтения runtime и удерживает его до shutdown. init-workspace использует тот же lock. Для запуска требуется утилита flock (в Nix package она добавлена в runtime PATH).
Текущее состояние и дальнейшие направления
Реализованы sandboxed файловые tools, Git lifecycle, HTTP/CLI/Matrix, persisted события и уведомления, allowlisted web/RSS fetch, вложения, ограниченные автономные задания, timezone-aware ежедневное расписание, crash recovery, workspace lock и persisted native Pi sessions.
Возможный дальнейший backlog, только при подтверждённой необходимости:
- CI с TypeScript, test, format и coverage gates.
- Retention для runtime events и Pi session JSONL.
- Lifecycle либо отдельное blob-хранилище для вложений.
- Более строгая SSRF-защита и сетевой sandbox.
- Интеграция с внешним календарём с заранее выбранным source of truth.
- Ограниченный script execution в отдельном sandbox.
- Web search поверх отдельной политики доступа.
Система считается успешной, если пользователь может естественным языком вести планы, проекты и устойчивые предпочтения, а агент использует существующую структуру Markdown/Org, не выходит за workspace, оставляет понятный Git diff, продолжает сессию после рестарта и безопасно сериализует foreground/background работу — без обязательной предметной схемы проектов и задач.
Проверки
npm run check
npm test
Лицензия
Проект распространяется по лицензии GPL-3.0-only.