Skip to Content
Card SDKСправочник APIАгенты и терминалы

Агенты и терминалы

Список агентов

Нужно разрешение agents.read.

import type { AgentInfo } from '@neurosquad/card-sdk' const agents: AgentInfo[] = await card.agents.list() const waiting = agents.filter((a) => a.kind === 'ai' && a.status === 'needs-input') const one = await card.agents.get(agents[0].id) render(waiting.map((a) => a.name), one.model ?? 'default model')

Все ИИ-агенты и терминалы воркспейса карточки (другие карточки агентами не считаются):

ПолеТипЗначение
idstringИдентификатор карточки на холсте.
namestringТо, что видит пользователь.
harnessstringclaude-code, codex-cli, opencode, qwen-code, shell-bash, shell-powershell, shell-cmd…
kind'ai' или 'shell'ИИ-агент или терминал.
statusAgentStatusworking, needs-input, finished, idle или exited.
connectedbooleanС вашей карточкой его соединяет стрелка.
sessionTitlestring?Заголовок, который агент дал своей сессии.
turnStartedAtnumber?Время начала текущего хода (мс с эпохи), пока статус working.
lastTurn{ startedAt, endedAt, endedAs }?Последний завершённый ход с момента запуска приложения: время в мс с эпохи (startedAt может быть null) и endedAs — finished или needs-input; чтобы догнать ходы, пропущенные, пока карточка не работала.
modelstring?Модель, на которой он работает, если известна.

Статусы берутся из того же трекера, что и в остальном приложении, — для Claude Code из его собственных хуков (для OpenCode и Kilo Code — из плагина NeuroSquad, для Hermes Agent — из его вебхуков жизненного цикла), поэтому needs-input («ждёт вашего разрешения») и finished различаются точно.

События

card.agents.onStatus(({ agentId, status }) => render(agentId, status)) card.agents.onTurn(({ agentId, phase, at }) => render(agentId, phase, new Date(at))) card.agents.onChanged((agents) => render(agents.length)) // added, removed or renamed // Only one agent: card.agents.onStatus((e) => render(e.status), agentId)
  • agents.status — { agentId, status, at } при каждой смене статуса.
  • agents.turn — { agentId, phase: 'start' | 'end', at }. Ход начинается, когда промпт отправлен, и заканчивается, когда агент закончил или ждёт ввода.
  • agents.changed — { agents }, весь новый список.

SDK подписывается, пока у вас есть слушатели. Каждый метод возвращает функцию, которая убирает слушателя.

Чтение вывода

Нужно разрешение agents.output и стрелка между вашей карточкой и агентом или терминалом.

// The visible screen, as text (up to 500 lines). const screen = await card.agents.readScreen(agentId, 80) // The last reply, from the agent's own session log (Claude Code, OpenCode, Kilo Code, Hermes Agent). null for others. const { text, at } = await card.agents.lastReply(agentId) // Live output: ANSI colours stripped, delivered in chunks about every 100 ms. const stop = card.agents.onOutput([agentId, shellId], ({ agentId: from, text: chunk }) => { if (chunk.includes('FAIL')) render(`${from} printed a failure`) }) stop() // when you no longer need it render(screen, text, at)

Для агента без стрелки эти вызовы падают с NOT_CONNECTED. readScreen для незапущенного агента падает с UNAVAILABLE.

Промпты агентам

Нужно разрешение agents.prompt и стрелка к ИИ-агенту (терминалам отправляют команды).

import { isCardSdkError } from '@neurosquad/card-sdk' try { const delivered = await card.agents.prompt(agentId, 'Run the checkout tests and fix what fails.') // 'sent' — submitted now // 'queued' — the agent is working; it goes out when the turn ends // 'inserted'— typed in only (submit: false); the user presses Enter card.ui.toast(delivered === 'queued' ? 'Queued after the current turn' : 'Sent') } catch (error) { if (isCardSdkError(error, 'NOT_CONNECTED')) card.ui.toast('Draw an arrow from me to an agent') else if (isCardSdkError(error, 'BUDGET_PAUSED')) card.ui.toast('The workspace is over its budget') else if (isCardSdkError(error, 'USER_CANCELLED')) card.ui.toast('Not sent') else throw error }

Параметры — card.agents.prompt(agentId, text, { submit, whenBusy }):

ПараметрПо умолчаниюЗначение
submittruefalse набирает текст в поле ввода агента, не нажимая Enter, — пользователь проверяет и отправляет сам. Результат inserted.
whenBusy'queue'Пока агент работает: queue ставит в очередь промптов агента (queued), send отправляет всё равно (sent), fail отказывает с BUSY.

Что приложение делает с каждым промптом:

  • Он идёт тем же путём, что и очередь промптов и бюджет: если воркспейс вышел за бюджет, вызов падает с BUDGET_PAUSED (человек, набирающий руками, не ограничен — ваша карточка ограничена).
  • Он виден на стрелке и в её журнале с именем вашей карточки.
  • Агенту в опасном режиме приложение сначала показывает промпт в своём диалоге на всё окно и ждёт Send prompt; если пользователь откажется — USER_CANCELLED; если вашей карточки нет на экране — NOT_VISIBLE.
  • Не больше 6 промптов в минуту на карточку — вместе с промптами, отправленными через порт агента prompt, — до 20 000 символов каждый.
  • Промптов от карточек в очереди — не больше 10 на агента; видно, от какой карточки каждый, и они выбрасываются до отправки, если пропала стрелка, разрешение, сама карточка или её пакет или агента перевели в опасный режим.

Промпт — это указание тому, кто может править файлы и запускать команды. Никогда не пересылайте в промпт текст из интернета, файла или другой карточки, не показав его сначала пользователю, — именно так и происходит внедрение промптов (prompt injection). Если текст не ваш, лучше submit: false.

Прогон одного агента

С NeuroSquad 0.1.264 (SDK 1.1). Нужно разрешение usage.read и стрелка к ИИ-агенту — агент без стрелки даёт NOT_CONNECTED, оболочка — INVALID_PARAMS. Не чаще 60 вызовов в минуту на карточку. Сначала проверьте card.host.supports('agents.usage'): старое приложение ответит METHOD_NOT_FOUND.

const run = await card.agents.usage(agentId, since /* мс эпохи */, until /* необязательно, по умолчанию сейчас */)

Возвращает цифры этого агента в окне — те же, из которых строится раздел «Расход» приложения. Ваш фрейм может стоять на паузе или быть выгружен, пока агент работает: историю статусов ведёт само приложение, поэтому время считается верно.

ПолеЗначение
promptsСколько ходов дали агенту. Ответ на запрос разрешения продолжает тот же ход; завершённый ход без единого запроса к модели (мигание статуса) не считается.
requestsЗапросы к модели (обращения к API), записанные харнессом в окне.
inputTokens / outputTokensНекешированный ввод; вывод вместе с рассуждениями (reasoningTokens — их часть).
cacheReadTokens / cacheWriteTokensРабота с кешем — там, где харнесс и провайдер о ней сообщают.
totalTokensВвод + вывод + чтение кеша + запись в кеш.
elapsedMsОт первого промпта до последнего завершения (до текущего момента, пока идёт ход).
workingMsСколько агент работал; ожидание пользователя не входит.
costMicroUsdСтоимость, записанная харнессом, иначе по прайсу, с округлением вверх; null, если цена неизвестна (в том числе у модели на собственном сервере пользователя).
costPartialtrue, если у части запросов нет цены — стоимость покрывает только те, что с ценой.
usageReadablefalse у харнесса, чей журнал расхода приложение не читает (Amp, Cursor): все поля токенов — null, время считается.
firstPromptAt, lastFinishedAtМс эпохи или null.
model, provider, status, timingComplete, scannedAtКонтекст для цифр. timingComplete — false, если история статусов приложения начинается позже since (приложение или агент запущены позже); scannedAt — когда журналы читались последний раз.

null значит не сообщается: в журнале харнесса нет такого числа (некоторые не пишут запись в кеш или рассуждения), локальный сервер не прислал счётчики кеша или приложение вообще не читает журнал расхода этого харнесса (Amp, Cursor). Показывайте «не сообщается», а не 0. Токены — точные целые числа.

Запросы на шкале времени

С NeuroSquad 0.1.264 (SDK 1.2). То же разрешение и та же стрелка, что у agents.usage; проверьте card.host.supports('agents.timeline').

const t = await card.agents.timeline(agentId, since, until /* необязательно */) for (const r of t.requests) draw(r.startedAt ?? r.endedAt, r.endedAt, r.outputTokens, r.tools)
ПолеЗначение
requests[]Каждый запрос к модели, от старых к новым: endedAt (всегда), startedAt (если журнал харнесса пишет, когда запрос отправлен, иначе null), токены (null — не сообщается), tools (вызовы инструментов, которые запросила модель).
requestTimesstart-end — у запросов есть время отправки (рисуйте полосы); end — только время завершения (рисуйте отметки); none — журнала нет.
statuses[]Статус агента подряд идущими отрезками (working, needs-input, finished, idle, exited).
turns[]Промпт → завершение; counted: false — ход без единого запроса (всплеск статуса).
truncatedВ окне больше 2000 запросов: оставлены самые новые.

Терминалы

Нужно разрешение terminals.write и стрелка к карточке терминала (bash, PowerShell или cmd).

// Run a command and wait for it to finish. const result = await card.terminals.run(shellId, 'npm test -- --reporter=dot', { timeoutMs: 120_000 }) if (result.timedOut) render('still running after 2 minutes') else render(result.exitCode === 0 ? 'passed' : `failed with ${result.exitCode}`, result.output) // Or just type into it (and press Enter). await card.terminals.write(shellId, 'git status', { submit: true })
run(agentId, command, { timeoutMs? })write(agentId, text, { submit? })
ДелаетВыполняет одну команду и ждёт, пока вернётся приглашениеНабирает текст; submit: true нажимает после него Enter
Возвращает{ exitCode, output, truncated, timedOut }ничего
Лимитытайм-аут 1–120 с (по умолчанию 120), вывод до 64 КБ; 30 команд в минуту на карточку, вместе с портом терминала command60 в минуту

В cmd exitCode равен null — cmd его не сообщает. Пользователь видит каждую команду в самом терминале и на стрелке. Бюджет агентов на команды не распространяется — терминал не ИИ-агент, — но они выполняются со всеми правами пользователя: см. чек-лист безопасности.