npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

sagent-code

v2.57.0

Published

AI agent that streams JavaScript into a persistent sandboxed Node.js runtime

Readme

sagent-code

Локальный AI-агент, у которого каждый ответ модели — это исполняемый JavaScript. Host приводит ответ к исполнимому виду и выполняет код в постоянном sandboxed Node.js-процессе, где состояние сохраняется между итерациями.

Вместо фиксированного набора tool-call'ов модель получает целый язык: она пишет код, видит результат выполнения и пишет следующий. Инструменты (dsl.*) — обычные функции внутри рантайма, а не протокол вызовов.

Как это выглядит

Ответ модели целиком:

const files = await dsl.file.rg("executeTask", { glob: "src/**/*.js" });
if (files.matches.length === 0) {
  dsl.print({ status: "not found" });
} else {
  dsl.print.user("Нашёл " + files.matches.length + " совпадений.");
  dsl.end();
}

Claude Code подключается без передачи credentials в SAgent: установите и авторизуйте Claude Code обычным способом, затем включите "claudeCode": "enabled" и выберите provider claude_code в ~/.sagent/settings.json. Модели и уровни reasoning будут получены через Claude Agent SDK. Подробности — в doc/configuration.md.

dsl.end() завершает рабочий цикл только если реально выполнился. Пока его не было, агент формирует следующую итерацию и отдаёт модели результат прошлой.

Требования

  • Windows x64 — исполнение идёт через собственную песочницу sagent-sandbox-runner.exe из bin/sagent-sandbox-win (исходники на Rust в sandbox/), других платформ рантайм не поддерживает.
  • Node.js ≥ 22 (package.jsonengines).
  • Авторизация ChatGPT/Codex, локальная сессия Claude Code либо ключ OpenAI-compatible провайдера.

Установка

npm install -g sagent-code

Появляется команда sagent-code (короткий алиас — sagent2). Для работы из исходников вместо этого достаточно npm install в корне репозитория.

CLI работает прямо из каталога установки npm. Песочница добирается до %APPDATA%\npm\node_modules через stat_paths раннера: родительские каталоги получают ненаследуемый грант «только атрибуты», которого хватает для резолва модулей, но который не открывает ни их содержимое, ни профиль. Старое зеркалирование в ~/.sagent/app (и переменная SAGENT_APP_HOME) убраны — если каталог остался от прежних версий, его можно просто удалить.

ChatGPT-подписка включена по умолчанию и переиспользует credentials Codex CLI из ~/.codex/auth.json. Если их ещё нет:

codex login

Альтернатива — объявить OpenAI-compatible провайдера в ~/.sagent/settings.json, а ключ положить в ~/.sagent/.env. Формат и рабочий пример — в doc/configuration.md.

Запуск

Агент работает с проектом в текущем каталоге, поэтому запускать его нужно из корня нужного проекта. Терминальный режим:

sagent-code

Команда без ключей поднимает HTTP-сервер с веб-панелью (порт 3210 или следующий свободный) и печатает URL панели и Bearer-токен. Терминального чата нет: вся работа идёт в панели, консоль остаётся для логов. Другой порт:

sagent-code --http 4000

Из исходников те же команды выглядят как node src/agent.js [флаги].

Флаги: --http [port], --host <host> (по умолчанию 127.0.0.1), --version, --help. Ключ --http-only и переменная SAGENT_HTTP_ONLY устарели, но принимаются и ничего не меняют. Установленная версия видна по sagent-code --version и в шапке HTTP-панели. Остановка — Ctrl+C.

При первом запуске создаются ~/.sagent/settings.json и ~/.sagent/.env, а также примеры settings.example.json и .env.example — примеры обновляются вместе с пакетом, когда несут более новую редакцию (штамп даты внутри файла); сами settings.json и .env никогда не перезаписываются.

Каналы вывода

Три канала строго разделены — это центральная часть контракта:

| Вызов | Кому видно | Попадает в контекст модели | Завершает цикл | | --- | --- | --- | --- | | dsl.print(value) | только модели | да, как [prints], остаётся в истории | нет | | dsl.print.once(value) | только модели | только следующий запрос, блоком [once], мимо истории | нет | | dsl.print.user(value) | только пользователю | нет | нет | | dsl.end() | — | — | да, при фактическом выполнении |

Ответ пользователю — это GitHub Markdown, в котором разрешены HTML и инлайновый SVG: таблицы, <details>, бейджи, диаграммы, картинки из проекта через <img src="doc/chart.png">. Санитайзер вырезает <script> и обработчики событий, поэтому интерактивность строится на CSS. Блок <style> разрешён и автоматически ограничивается своим сообщением: селекторы получают префикс контейнера, так что body, html и * внутри него означают само сообщение, а переоформить панель из ответа нельзя. Набор классов Tailwind, доступный модели, закреплён safelist'ом (@source inline в http/src/style.css) и перечислен в системном промпте — иначе сборщик вырезал бы неиспользуемые утилиты и разметка ответа приезжала бы без стилей.

dsl.print.once — канал для данных, которые нужны ровно один ход (файлы, диффы, выдача поиска): модель смотрит на них один раз, и в историю они не попадают. Канал выбирается по времени жизни данных, а не по их объёму: хвост запроса не кэшируется и оплачивается полностью при каждой доставке, поэтому то, что понадобится и через три хода, дешевле один раз отправить обычным dsl.print — оно станет частью кэшируемого префикса. При ошибке следующей ячейки данные доставляются повторно, пока какая-нибудь ячейка не выполнится без ошибок.

Инструменты

dsl.file — файловое семейство: read (диапазоны строк, grep с контекстом, дистилляция через ask-модель — файл не попадает в контекст главной модели), write (create-only по умолчанию; с { ask: true } содержимое генерирует ask-модель по короткому ТЗ), patch, rg, ls, exists, append, stat, rm. Все пути — внутри проекта, возвраты — структурные объекты. dsl.ask — запрос к отдельной быстрой модели; dsl.ask.oracle — project-wide вопрос по снимку групп исходников. dsl.viewImage, dsl.generateImage — работа с изображениями. dsl.chrome — управление Chrome по DevTools Protocol (вкладки, ввод, эмуляция, скриншоты, console/network/trace-мониторы). Ожидания строятся на условии, а не на паузе: waitFor(выражение) ждёт, пока условие в странице станет истинным, а click(цель, { until }) подтверждает эффект клика и повторяет его заданное число раз — элемент бывает видим раньше, чем фреймворк навесил обработчик. Забытые сессии наблюдения гасятся в конце задачи, с сообщением о том, что именно закрыто. dsl.fun и mem — persistent функции и данные, переживающие рестарт рантайма. dsl.code — оформление fenced-блоков для пользовательского Markdown.

Функции fun.watch_* — сенсоры: host вызывает их все параллельно перед каждой генерацией, и непустые возвраты приходят модели блоком [watch] строками имя: вывод, не попадая в историю. Приоритет задаётся числом (fun.watch_disk = 20 — по образцу описания строкой): больше — выше в блоке и первее при урезании бюджета. Модель сама пишет себе наблюдателей — постоянный маленький статус или одноразовое оповещение с самоотключением; ошибка или таймаут сенсора приходят в его строке коротким [watch error], не задерживая остальных. Состояние между опросами сенсор получает первым аргументом: это объект mem.fun.<имя функции>, приготовленный заранее, и его мутация уже есть сохранение — mem, как и fun, пишется после каждой завершившейся ячейки. Держать в нём стоит дельту, а не журнал, и никогда — handle: таймер или сокет в любом узле mem останавливает сохранение всего графа, о чём агент теперь сообщает вслух вместо прежнего молчания.

fun ведёт статистику использования: сколько раз функцию вызывали и правили (dsl.fun.get — по функции, dsl.fun.stats — суммарно и топ вызываемых). Счётчики переживают рестарт и сбрасываются при удалении функции; автоматические опросы watch_* вызовами не считаются.

dsl.git([...]) — единственный способ работать с репозиторием, кроме чтения: sandbox runner запрещает запись в .git, поэтому внутри песочницы не создаётся даже index.lock, а креденшелов к remote у неё нет намеренно. Команда исполняется на хосте, с профилем и правами пользователя, и по умолчанию выключена: доступ включается тумблером в панели с выбором уровня — «только чтение» (порцелан и плумбинг вроде log, diff, diff-tree, rev-list, cat-file, for-each-ref, плюс fetch и ls-remote) или «полный» (весь рабочий цикл вплоть до push, а также init, clone, патчи и обслуживание). На чтении разбирается и подкоманда: remote add, stash pop или worktree add требуют полного уровня, хотя сами команды на чтении доступны в своих читающих формах. Настройка живёт в ~/.sagent/settings.json, вне песочницы, поэтому модель не может выдать права себе. Хуки репозитория при исполнении отключены, подмена конфига на лету запрещена, каждый вызов печатается в терминал. Если репозиторий лежит не в корне проекта — подмодуль, vendor-каталог, отдельный репозиторий внутри дерева, — он адресуется вторым аргументом: dsl.git(["status", "--short"], { cwd: "vendor/lib" }). Путь считается от корня проекта, выход за его пределы отклоняется до запуска git, а каталог, в котором команда выполнилась, возвращается в поле cwd.

Авторитетный справочник живёт в самом рантайме: dsl.help() возвращает индекс тем, dsl.help("ask") — полную сигнатуру, ограничения и примеры конкретной команды. Документация проекта описывает только общую модель работы.

HTTP-панель

Слева — чат, состояние задачи, выбор провайдера/модели/reasoning, compact, interrupt, вложения и статистика контекста. В пикере основной модели включается «Основная модель 2»: тогда нечётные итерации задачи ведёт первая модель, чётные — вторая (счёт от начала каждой задачи, история и контекст у них общие). Справа — вкладка «План» (шаги текущей задачи; открыта по умолчанию), временные globals, persistent-функции (поиск, статистика вызовов/правок, приоритет, исходники), память, вкладка «DSL» со справкой dsl.help ровно в том виде, в каком её читает модель, файловое дерево, группы исходников, Oracle с параллельными запросами, журналом, выбранной моделью, 20-секундным прогрессом токенов и автопересборкой снимка не чаще раза в минуту, а также Chrome CDP.

Строка итерации начинается с номера хода #N — это счёт от начала истории, обнуляемый компактом: столько ходов агента сейчас лежит в контексте и перечитывается моделью в каждом запросе. Чип thinking раскрывает живой поток рассуждений модели: пока идёт thinking-фаза, блок открыт автоматически и сворачивается по завершении итерации; клик фиксирует состояние вручную. Рядом — once (токены и содержимое данных print.once, доставленных в запрос), watch (вход от сенсоров fun.watch_*), тройной счётчик печати print A/B/C (print / print.once / print.user) и чипы dsl N/fun N с поимённой разбивкой вызовов. Нулевые чипы скрыты, блоки работают аккордеоном; в финале задачи виден блок «Сэкономлено» — токены, ушедшие через print.once мимо истории.

Шестерёнка в шапке открывает настройки: подключение, автокомпакт, модели dsl.ask и Oracle, git вне песочницы. Автокомпакт и git включаются тумблером в строке заголовка карточки и сохраняются сразу, токен и модель dsl.ask применяет кнопка «Сохранить настройки» слева от «Закрыть». Автокомпакт включён по умолчанию: когда контекст заполнен до порога (по умолчанию 80%, регулируется в диапазоне 50–90%), история сжимается сама, а в ленте появляется строка «Автокомпакт: контекст заполнен на N% при пороге M%» — молча сжимать историю за пользователя нельзя. Порог проверяется после каждого хода, поэтому длинная задача сжимается на ходу и продолжается на сжатом контексте: план, mem и fun это переживают, детали из истории заменяются её пересказом.

Версия в шапке мигает, когда в npm появилась свежая: сервер спрашивает реестр при запуске и раз в час. Клик открывает подтверждение, по «Да» ставится npm install -g sagent-code@latest, процесс перезапускается сам, а страница перезагружается уже на новой версии. Обновление доступно, только когда агент свободен — npm заменяет файлы под работающим рантаймом.

Панель живёт во время хода: настройки, выбор моделей, редактор и отправка сообщений доступны, пока модель работает. Сообщение, отправленное на ходу, не заводит вторую задачу — оно встаёт в очередь и доезжает до модели следующей итерацией, а если dsl.end() уже выполнен, цикл продлевается на один ход, чтобы ответ на него всё-таки прозвучал. Заблокирован во время работы только переключатель прав: он гасит рантайм.

Сервер отдаёт production-сборку из корневого dist/. Этот каталог не хранится в git, поэтому после клонирования и после любых правок в http/ нужен npm run build:http — иначе панель вернёт 404. Маршруты и ограничения — в doc/http-api.md.

Токен генерируется на каждый запуск и печатается в консоль. Для клиента с 127.0.0.1 он намеренно не требуется — панель рассчитана на доверенную локальную машину. Для удалённого адреса токен обязателен, а явно заданный SAGENT_HTTP_TOKEN проверяется и локально.

Изоляция

Уровень изоляции выбирается переключателем прав левее выбора основной модели: «через песочницу» (по умолчанию) или «полный доступ». Полный доступ — это буквально отсутствие песочницы: worker запускается напрямую, с вашим окружением и правами, и всё описанное ниже к нему не относится. Уровень фиксируется при запуске процесса, поэтому смена останавливает рантайм — следующая задача поднимет его заново, fun и mem это переживают. Значение хранится в ~/.sagent/settings.json (permissions), вне песочницы, поэтому модель не может выдать права себе.

Под песочницей Node-worker запускается через sagent-sandbox-runner.exe с политикой текущего проекта — это единственный слой изоляции, сам Node работает без ограничений. Запись разрешена только в корень проекта, чтение — в проект, каталог пакета и родительские каталоги до границы профиля (нужны Node для резолва модулей), а ~/.ssh, ~/.aws, ~/.azure, ~/.kube, ~/.config/gcloud и CODEX_HOME попадают в deny-list. Домашний каталог, AppData и %TEMP% не выдаются, поэтому проект не может находиться внутри них. Политика пишется в .sagent/sandbox-policy-<pid>-<агент>-<nonce>.json — файл на сессию, а не на процесс: при смене прав старая сессия гасится в фоне и удаляет свой файл, и общее имя означало бы удаление политики уже поднятой новой сессии. Политики мёртвых процессов подчищаются при создании следующей.

Chrome запускается host-процессом вне песочницы (src/chrome-launcher.js), а рантайм только подключается к готовому CDP-эндпоинту. Иначе браузер унаследовал бы restricted token и ACL раннера, не смог бы прочитать свой каталог установки и не поднял бы CDP. Модельный dsl.chrome.ensure({ launch: true }) тоже работает: рантайм запрашивает запуск у host'а и ждёт готовый эндпоинт. Поднять браузер можно и кнопкой «Запустить Chrome» в панели или вручную с --remote-debugging-port.

Несколько агентов в одном браузере

Все экземпляры sagent-code делят один Chrome на одном CDP-порту с общим профилем ~/.sagent/chrome-profile. Каждый агент получает свой SAGENT_AGENT_ID и работает только в своих вкладках: dsl.chrome.tabs() показывает лишь их, обращение к чужому targetId отклоняется, а dsl.chrome.openTab() и closeTab() управляют собственными вкладками. Первая вкладка создаётся при первом обращении к браузеру; если endpoint не позволяет открывать вкладки (внешний Chrome, отданный только на чтение), агент забирает свободную существующую.

Владение вкладками ведёт host в ~/.sagent/chrome-tabs.json — песочница пишет только внутрь проекта и не может держать общий для машины реестр. Записи мёртвых вкладок вычищаются автоматически, поэтому упавший агент не занимает вкладку навсегда. close закрывает только вкладки своего агента и никогда не посылает Browser.close: Chrome с интерфейсом завершается сам, когда закрыта последняя вкладка. Поэтому один агент не выбивает браузер у остальных, а последний уходящий оставляет систему без лишних процессов. Субагенты получат собственные id тем же механизмом.

Исключение — headless: без интерфейса Chrome не применяет правило «последняя вкладка закрыта → выход» и остаётся ждать команд. Для headless-прогонов процесс нужно завершать самостоятельно.

Изоляции между вкладками одного профиля нет: cookies, localStorage и логины общие для домена. Если агентам нужны разные сессии, им нужен разный SAGENT_CHROME_USER_DATA_DIR вместе с разным SAGENT_CHROME_DEBUG_URL.

Файлы, результаты поиска, логи, DOM, browser console и сетевые ответы считаются недоверенными данными: модель их анализирует, но не исполняет как инструкции.

Данные проекта

Всё состояние агента лежит в $project/.sagent/:

| Путь | Что это | | --- | --- | | history.jsonl | активная история | | history-<дата>_<время>.jsonl | архивы после /compact | | fun.ini | исходники persistent-функций и описания | | mem.ini | persistent-граф данных | | plan.ini | план текущей задачи (dsl.plan): переживает рестарт и /compact, правится руками; выполненный уезжает в plan-<дата>_<время>.ini | | function/ | файловые инструменты модели | | upload/ | вложения из HTTP UI |

Структура репозитория

src/          host-агент, модели, история, HTTP и sandbox launcher
src/runtime/  persistent Node worker, DSL, Chrome CDP, state persistence
http/         исходники Vue/Vite/Tailwind панели
dist/         production-сборка панели (генерируется, не в git)
bin/          CLI-обёртки и собранная песочница sagent-sandbox-win
sandbox/      исходники песочницы на Rust (npm run build:sandbox)
scripts/      вспомогательные CDP-скрипты
tests/        автономные Node.js тесты
doc/          документация

Разработка

npm run dev:http
npm test

npm test сначала собирает панель, затем последовательно прогоняет тесты runtime, истории, настроек, model routing, HTTP, изображений, Chrome и модельного цикла.

Правила изменений (синхронизация runtime-протокола, документирование новых DSL-команд, safe projection для публичных HTTP-полей) — в doc/development.md.

Документация

Лицензия

MIT