Пређи на садржај
BeeuniBeeuni
Српски
Esc
↑↓навигација↵отвори⌘Jпреглед
На овој страници

Сесије

Покрените сесију, стримујте њене догађаје и обрадите грешке које може да избаци.

Сесија је једно извршавање агента, у сопственом изолованом окружењу, на сопственој git грани. beeuni.session(projectId, sessionId) враћа објекат за управљање свиме што сесија ради: покретањем, слањем упита, стримовањем догађаја и читањем статуса. Ова страница обухвата тај објекат, потврду спремности, стримовање и типизиране грешке које SDK позив може да изазове.

const s = beeuni.session(projectId, sessionId);

s је објекат за управљање свиме што сесија ради. ID сесије, ID изолованог окружења и назив гране имају исту вредност. Погледајте Сесије за објашњење концепта.

Животни циклус сесије

Метод Обавија Шта ради
s.get(opts?) GET /projects/:pid/sessions/:sid Чита детаље сесије
s.update(input) PATCH …/sessions/:sid Преименује сесију, замењује њене ознаке или спаја кључеве метаподатака
s.start(waitMs?) POST …/sessions/:sid/start Обезбеђује и покреће извршно окружење
s.restart() POST …/sessions/:sid/restart Поново покреће извршно окружење; задржава исто изоловано окружење
s.configState() GET …/sessions/:sid/config Чита актуелност конфигурације (stale) и стање издања конфигурације (release)
s.reloadConfig(input?) POST …/sessions/:sid/reload Поново компајлира конфигурацију агента и замењује извршно окружење након валидације
s.reloadConfigStream(input, onEvent) POST …/sessions/:sid/reload-stream Обавља исто поновно учитавање и емитује фазе напретка које је сервер потврдио
s.stop() POST …/sessions/:sid/stop Зауставља извршно окружење; сесија остаје
s.delete() DELETE …/sessions/:sid Брише сесију
s.setSharing(intent) PUT …/sharing Подешава дељење и видљивост
s.participants() GET …/sessions/:sid/participants Чита ко може да отвори сесију и ко је послао сваки упит
s.watch() GET …/sessions/:sid/watch Чита да ли добијате обавештења о овој сесији. Захтева notification_center; 403 feature_disabled док је искључено
s.setWatch(watching) PUT …/sessions/:sid/watch Укључује праћење (true) или утишава (false) обавештења сесије за вас. Захтева notification_center; 403 feature_disabled док је искључено
s.presence({ tab_id, active, alerts? }, { keepalive? }) PUT …/sessions/:sid/presence Пријављује отворену картицу. alerts: true означава да картица приказује сопствена обавештења, па се push обавештење на телефону задржава. Када је notification_center искључено, свака присутна картица га задржава. keepalive омогућава да захтев траје и након затварања странице; подразумевано је укључено за пријаву одсуства
s.cost() GET /usage/session-costs/:sid Чита коначне трошкове LLM-а и рачунарских ресурса без покретања извршног окружења
s.scope() GET …/sessions/:sid/scope Чита сачувано сужење опсега тајни и материјализована везивања конекција
s.rescope(input) PUT …/sessions/:sid/scope Замењује наведена поља опсега за следећи упит или позив алата
s.commit(input?) — Прави комит са радом агента
s.providerSecretPool.get(providerId) GET …/provider-secret-pools/:providerId Чита ID-ове изабраних тајни налога
s.providerSecretPool.list() GET …/provider-secret-pools Наводи конфигурисане групе, укључујући празне, и пријављује can_edit
s.providerSecretPool.set(providerId, secretIds) PUT …/provider-secret-pools/:providerId Замењује избор; null враћа подразумевану вредност пројекта

Ознаке и метаподаци

Свака сесија садржи labels: string[] и објекат metadata. Користите их да класификујете сесије и одредите како их ваша апликација приказује.

  • Ознаке су слободног облика: свакој се уклањају размаци на почетку и крају, има 1–64 знака, највише 20 по сесији, а дупликати се уклањају. Поклапање је тачно и разликује велика и мала слова.
  • Метаподаци су JSON објекат слободног облика за ваше кључеве, са највише 16.384 знака JSON-а по упису. Сервер ту чува и сопствене кључеве (custom_name, source, …); захтев који наводи кључ којим управља сервер одбија се са 400. Додајте префикс својим кључевима (app_…) да бисте избегли сукоб са њима.
// Set both at create.
const project = beeuni.project(projectId)
const created = await project.sessions.create({
  initial_prompt: 'Triage ticket T-142',
  labels: ['support', 'tier:gold'],
  metadata: { app_ticket: 'T-142', app_priority: 2 },
})

// Update later. `labels` replaces the list; `metadata` merges keys and a
// `null` value removes that key.
const s = beeuni.session(projectId, created.session_id)
await s.update({ labels: ['support', 'resolved'], metadata: { app_priority: null } })

// List by label, server-side: a session must carry EVERY given label.
const { items } = await project.sessions.listPage({ labels: ['support', 'resolved'] })

// Render conditionally.
for (const session of items) {
  const badge = session.labels?.includes('tier:gold') ? 'Gold' : null
  const ticket = session.metadata.app_ticket as string | undefined
}

У React-у проследите labels у useProjectSessions(projectId, { labels }); сваки скуп ознака добија сопствени унос у кешу. Агенти постављају иста поља из CLI-ја (beeuni sessions new --label … --meta k=v, beeuni sessions update --label …) и са хостованог MCP сервера (start_session labels / metadata, list_sessions labels). Верзија која се може покренути је packages/sdk/examples/12-session-labels.ts.

Када су pooled_provider_secrets и llm_gateway омогућени, сесија може да изабере више доступних тајни налога за једног добављача. Направите ресурс са опсегом пројекта путем beeuni.accounts.secretResources.create(accountId, { project_id: projectId, ...input }). Сваки члан пројекта подразумевано може да га користи. Ограничите га помоћу .setAccess(accountId, secretId, 'members', userIds). Читања враћају метаподатке и додељене дозволе, никада вредност тајне. Након 429 добављача, пре почетка излаза, мрежни пролаз покушава са другим изабраним кључем.

await s.providerSecretPool.set('anthropic', [primarySecretId, backupSecretId]);
const selected = await s.providerSecretPool.get('anthropic');
await s.providerSecretPool.set('anthropic', null); // inherit project default

Користите метод са стримовањем када позивалац приказује напредак поновног учитавања:

await s.reloadConfigStream({ refresh_repo: false }, (event) => {
  if (event.type === 'phase') console.log(event.phase);
});

Фазе су checking-session, refreshing-workspace, compiling-config, applying-config и confirming-config. Сервер изоставља refreshing-workspace када је refresh_repo false. Фаза applying-config укључује замену извршног окружења коју је демон валидирао.

s.configState() враћа release када API подржава издања конфигурације. Његов тип је SessionConfigRelease. Резултати поновног учитавања садрже исти објекат.

const { stale, release } = await s.configState();
if (release?.fallback_reason) {
  // The desired config failed on the box. An earlier config runs the session.
  console.warn(release.fallback_reason, release.running_release_id);
} else if (stale === true) {
  // Moves the session onto the base branch's current config AND
  // fast-forwards its /workspace checkout. `workspace_checkout` says what
  // happened to the checkout; `detail` words both halves.
  const result = await s.reloadConfig();
  console.log(result.workspace_checkout, result.detail);
}

stale има три стања. null значи да Beeuni није могао да утврди стање. Проверавајте stale === true, никада !stale. Старији API изоставља release. Погледајте Усаглашавање конфигурације.

s.participants() враћа { participants, total, multi_user }. participants наводи људе који сада могу да отворе сесију, прво власника, највише 20, сваки { user_id, name, email, avatar_url, is_viewer }; total је укупан број. multi_user је true када две или више особа могу да отворе сесију. У React-у, useSessionParticipants(projectId, sessionId) чита исте податке. Ко је написао сваку поруку означено је са s.messageAuthors().

Још три метода за читање употпуњују објекат за управљање:

  • s.previews() — потенцијални портови за преглед које извршно окружење излаже.
  • s.publicShares.list() / .create(input) / .revoke(shareId) — јавни линкови за дељење.
  • s.reminders.list() / .create(input) / .update(reminderId, { enabled }) / .remove(reminderId) — заказани упити за ову сесију. Погледајте Подсетнике. .create({ transcript: true }) прави линк за читање разговора (један активан линк по сесији; други позив враћа исти линк). Читајте га анонимно помоћу getPublicSessionShareMessages(token); findActiveTranscriptShare(shares) бира активни из .list().
  • s.audit(limit?) — ревизорски траг радњи агента у сесији.
  • s.attachments.upload(file, options?) — сачувајте приватну датотеку пре покретања. Највише 50 MiB. Враћа стабилан URL за део упита који садржи датотеку.
  • s.attachments.read(attachmentId, signal?) — прочитајте сачуване бајтове као Blob без покретања изолованог окружења.
  • s.transcript(options?) — сажет транскрипт на серверу (текст и позиви алата, без улаза или излаза алата). Ово ради са токеном сесије ограниченим на пројекат.
  • s.transcriptSync(options?) — транскрипт који сервер чува на крају сваког потеза, у облику runtime омотача порука. Сваки део се чува 1 — текст, резоновање и сваки позив алата са улазом, излазом, грешком и метаподацима — осим бајтова прилога. Одговара док је изоловано окружење заустављено или се још буди, без његовог покретања. Проследите limit (највише 500) за најновији прозор, а next_cursor тог прозора као before да прочитате прозор старији од њега. Прозор се такође завршава на 1.000.000 знакова JSON-а: задржава најновије поруке, а next_cursor тада означава најстарију задржану поруку. Проследите child (ses_… id подагента, сесију коју отвара ред подагента) да прочитате сачувани транскрипт тог подагента; чува се сваки подагент који је разговор покренуо. source је mirror; complete је true само када прозор садржи све сачуване поруке и сачувана копија досеже прву поруку сесије. Сесија чије је извршно окружење прочитано у целости и није садржало поруке враћа available: true, complete: true, total: 0 и ниједну поруку. Позив алата сачуван пре 2026-09-28, када је сачувана историја изостављала улазе и излазе алата, враћа се са улазом који потврђују његов наслов или метаподаци, а команда и са својим излазом.
  • s.voiceTranscript(options?) — транскрипт активног гласовног позива ове сесије (изговорени потези и ask_beeuni/run_command позиви алата радника). Враћа празну листу када сесија нема активан позив, а не 404.

Датотеке које је сесија сачувала

findSessionAttachments(messages) наводи сваку сачувану датотеку на коју транскрипт упућује, редом и сваку само једном: датотеку коју је корисник отпремио, <file> референцу у упиту или копију датотеке коју је агент приказао, а коју чува сачувана историја. Чита и активни и сачувани транскрипт и никада не баца изузетак због неисправног транскрипта.

import { findSessionAttachments } from '@beeuni/sdk'

const saved = await s.transcriptSync({ limit: 500 })
for (const file of findSessionAttachments(saved.messages)) {
  const blob = await s.attachments.read(file.attachment_id) // no sandbox needed
  console.log(file.filename, file.role, blob.size)
}

Сваки резултат је { url, attachment_id, filename, mime, message_id, role }. role је user за отпремљену датотеку и assistant за датотеку коју је агент приказао; mime је null за потоњу, а бајтови носе сопствени тип.

Ко је написао сваку поруку

Свака порука има аутора. Сервер га чита из акредитива којим је послат упит: члан пројекта или агент друге сесије. Транскрипт извршног окружења га не садржи.

const { authors, initial_author } = await beeuni.session(projectId, sessionId).messageAuthors()
// getSessionMessageAuthors(projectId, sessionId) is the same call as a function.

const author = authors[runtimeMessageId] // undefined when the sender is unknown

authors користи id поруке извршног окружења као кључ. initial_author је родитељска сесија за прву поруку покренуте сесије. Вредност је null када сесија није имала први упит. Сваки аутор је SessionMessageAuthor:

kind Поља
'member' user_id, name, email (string | null)
'session' session_id, name (наслов сесије), agent (агент који покреће, када је именован)

Који модел је одговорио у потезу

Порука транскрипта наводи модел затражен за њен потез. Када ланац резервних модела пројекта одговори помоћу другог модела, само запис захтева на мрежном пролазу садржи модел који је одговорио и оно што је Beeuni наплатио.

const { latest, billed_cost, turns } = await beeuni.session(projectId, sessionId).modelUsage()
// getSessionModelUsage(projectId, sessionId) is the same call as a function.

latest?.served_model   // 'glm-5.3-flash': the model that answered the newest request
latest?.fallback_from  // 'codex/gpt-6.1-sol' when a fallback model answered, else null
turns[userMessageId]   // { served_models, fallback_from, billed_cost } for one turn

latest је null пре првог одговора. billed_cost је износ у USD који је Beeuni наплатио за позиве модела у сесији. turns користи као кључ id поруке извршног окружења за упит који је започео сваки потез. served_models наводи моделе који су одговорили у том потезу, прво оне са највише захтева. У React-у, useSessionModelUsage(projectId, sessionId) чита исти запис; позовите његов refetch() када се потез заврши.

У React-у, useSessionMessageAuthors(projectId, sessionId, messageCount) чита исте податке. Проследите број порука у транскрипту. Нова порука је једини догађај који додаје аутора, па упит поново преузима податке када се број промени.

Спремност захтева руковање

Пре него што пошаљете упит, позовите ensureReady(). Он обезбеђује изоловано окружење ако је потребно, чека да се извршно окружење покрене и враћа разрешено извршно окружење.

const { runtimeSessionId, runtimeUrl, sandboxId } = await s.ensureReady();

При хладном покретању, ensureReady() може да баци RUNTIME_UNAVAILABLE. Погледајте Поновни покушај при хладном покретању да сазнате шта то значи и како да покушате поново.

s.send() и s.abort() позивају ensureReady() уместо вас.

Поставите почетну, серверски одобрену везу са сесијом извршног окружења

React хост који се рендерује на серверу може да достави везу са сесијом извршног окружења која је већ трајно сачувана за исту Beeuni сесију:

const session = useSession(projectId, sessionId, {
  initialRuntimeSessionId: persistedSession.runtime_session_id ?? persistedSession.opencode_session_id,
});

Почетна вредност само хидрира кеширани садржај транскрипта док /start ради. Она не замењује идентитет извршног окружења. Веза коју враћа /start је меродавна и замењује застарелу почетну вредност.

opencode_session_id је застарели назив за runtime_session_id. Оба носе исту вредност; читајте стари назив само као резервну опцију приказану изнад.

Не прихватајте ову вредност из непоузданог селектора закупца. Не правите сесију извршног окружења у хосту. Beeuni прави и трајно чува коренску сесију. Кешеви упита извршног окружења и контролери транскрипта ограничени су на извршно окружење изолованог окружења, па једнаки id-ови сесија извршног окружења из различитих изолованих окружења не деле ставке кеша.

Поновни покушаји порука на чекању задржавају URL изворног изолованог окружења када промените сесију. Одговор на поруку 404 или 410 зауставља аутоматске поновне покушаје без брисања транскрипта. Експлицитно усаглашавање може поново да покуша читање и врати нормалну синхронизацију након успешног одговора.

Пошаљите упит

s.setModel({ providerID, modelID }); // sticky for later send() calls
s.setAgent('build'); // sticky for later send() calls

await s.send('Refactor the auth module');
await s.send('One-off task', { model, agent }); // overrides for this call only
await s.abort(); // stop the current run

send() ставља упит у трајно пријемно сандуче упита сесије (POST .../prompts), путању коју користе веб уређивач порука и CLI. Завршава се када је упит сачуван, са редом пријемног сандучета (prompt_id, state, message_id), а не када се потез заврши. Прочитајте одговор из s.stream() или транскрипта.

За runtime REST сесије, први send() на објекту сесије чита модел и агента трајно сачуване у Beeuni сесији. То спречава runtime сесију наслеђену из снимка да поново користи застареле подразумеване вредности снимка.

Приоритет избора за упит је:

  1. Позив send().
  2. Вредност setModel() или setAgent() објекта сесије.
  3. Трајно сачувана подразумевана вредност Beeuni сесије.

setModel само бира шта ће следећи локални send затражити — никада не напушта објекат сесије. Да бисте трајно сачували нови модел за активну сесију на серверу, користите changeModel:

const { applied_live } = await s.changeModel('anthropic/claude-opus-4-8');

Промена ступа на снагу поновним покретањем извршног окружења, па се текући потез завршава. applied_live је true када је активна сесија одмах применила промену, а false када се примењује при следећем покретању. Само власник сесије или позивалац са дозволама менаџера пројекта може да промени модел; сви остали добијају 403.

send() разрешава извршно окружење, а затим му шаље упит. abort() зауставља текуће извршавање без брисања сесије.

Опсег сесије и трошак

Прочитајте сачувано сужење приступа тајнама и материјализоване везе конекција. secrets_allowlist: null значи да важи одобрење приступа тајнама за агента:

const scope = await s.scope();
scope.connector_bindings_configured; // false = inherits the project defaults

connector_bindings је РАЗРЕШЕНА мапа, па изгледа исто за сесију која је заменила своје конекторе и за ону која наслеђује подразумеване вредности пројекта. Прочитајте connector_bindings_configured да бисте их разликовали пре приказивања опсега или његовог слања назад.

Замените једно или оба поља опсега:

await s.rescope({
  secrets: ['DATABASE_URL'],
  connector_bindings: {
    github: { connection_id: connectionId },
  },
});

Свако наведено поље замењује целу своју претходну вредност. Изоставите поље да бисте га оставили непромењеним. Промене конекција важе од следећег позива алата. Уклањање тајне зауставља будућу испоруку, али не може да уклони већ откривену вредност из контекста модела или постојећег процеса.

Обе осе имају изричит начин враћања на подразумевану вредност. То није исто што и празна вредност:

await s.rescope({
  secrets: null, // inherit the agent's secret grant
  connector_bindings: null, // drop the override; inherit the project defaults
});

secrets: [] и connector_bindings: {} су супротна упутства: изричито „без тајни пројекта“ и „без иједног конектора“, укључујући подразумеване вредности пројекта. Сесија која пошаље {} уместо намераваног null подразумевано одбија приступ сваком алијасу који није навела.

Прочитајте обједињени запис о трошковима:

const cost = await s.cost();

Запис обједињује коначни трошак LLM-а, обрачунати трошак рачунарских ресурса изолованог окружења, употребу модела, укупан број токена, трајање обраде и ставке у књизи евиденције. s.cost() не позива ensureReady().

Статус извршног окружења и прегледи

Метод Враћа Употреба
s.health(init?) { status, ok, health, body } Проверите да ли је извршно окружење активно
s.previewUrl(port, path?) string Преузмите прокси URL за порт који је агент изложио
s.proxyUrl(url?) string | undefined Препишите localhost URL који је агент исписао
const { ok, health } = await s.health();
const url = s.previewUrl(3000, '/docs');

s.health() никада не баца изузетак. Позовите га било када, чак и пре него што сесија добије извршно окружење. s.previewUrl() и s.proxyUrl() захтевају разрешено извршно окружење — прво позовите s.ensureReady(), иначе бацају SessionNotReadyError. Погледајте Грешке спремности сесије.

Шта извршно окружење подржава

health.capabilities наводи функције сесије које извршно окружење пружа. Прочитајте једну помоћу runtimeSupports(capabilities, capability) и сакријте контролу када врати false. runtime сесија пружа свих десет; pi сесија пружа само session.subagents, а за остале одговара са 501 feature_not_supported (404 за конфигурациони документ). Извршни оквири садржи пуну матрицу за runtime и pi.

Могућност Функција
session.rewind Враћање на поруку и њено обнављање
session.compact Сажимање разговора на захтев
session.commands Команде пројекта са косом цртом
session.fork Гранање сесије код поруке
session.subagents Подређене сесије подагената
session.mcp MCP сервери на које се извршно окружење само повезује
session.todo Листа задатака извршног окружења
session.shell Команда љуске покренута као потез
session.attach Прикључивање сопственог терминалског клијента извршног оквира
session.config Конфигурациони документ извршног окружења (/global/config); агенти и модели у сваком случају долазе из пројекта
import { runtimeSupports } from '@beeuni/sdk';

const { health } = await s.health();
if (!runtimeSupports(health?.capabilities, 'session.compact')) hideCompact();

runtimeSupports враћа true пре него што стигне одговор о исправности, као и за извршно окружење које не наводи могућност session.*: демон направљен пре увођења могућности покреће runtime, који пружа све функције. У React-у користите useRuntimeSupports.

Одговор start такође садржи листу: SessionStartResult.capabilities се поставља помоћу stage: 'ready'. useSession је бележи, па провера могућности ради исправно пре него што прва провера исправности врати одговор.

Називи статуса

Сваки хост користи исти речник, па једно стање има исти назив на сваком екрану.

Извоз Употреба
sessionListStatus(session, reviewCount?) Сесија на листи: needs-you, starting, running, done, stopped, failed, legacy
SESSION_LIST_STATUS[status] { label, tone, description } за тај статус
sessionConnectionLabel(connection) Кратка ознака рачунара сесије или null за unknown и live
SESSION_NOTICE Реченица у уређивачу поруке док рачунар није спреман
turnRetryLabel(secondsLeft) Retrying in 5s, Retrying now или Waiting to retry када одбројавање није познато

Тонови су actionable, live, progress, muted и danger. Зелена је само за live и actionable; завршена сесија је muted. Статус који новији API пошаље, а ова верзија не познаје, приказује се као stopped, никада као failed.

Стримовање

Користите s.stream() за пријем догађаја уживо у скрипти или на серверу. У React апликацији уместо тога користите useSession — он управља целим животним циклусом сесије уместо вас.

s.stream() је ток догађаја за компатибилност са runtime REST-ом. Beeuni API га прослеђује из изолованог окружења. Не постоји засебна WebSocket крајња тачка. Транспорт је fetch са телом одговора у току, које се чита преко ReadableStream и TextDecoderStream. SDK управља поновним повезивањем, повећавањем интервала између покушаја и провером сигнала активности.

Стримујте сесију:

  1. Прво позовите ensureReady(). Извршно окружење не постоји док се изоловано окружење не покрене.
  2. Отворите ток пре слања поруке да не бисте пропустили ране догађаје.
  3. Пошаљите поруку.
  4. Затворите ток када видите session.idle.
const session = beeuni.session(projectId, sessionId);
const { runtimeSessionId } = await session.ensureReady();

const stream = await session.stream({
  onEvent: (event) => {
    if (event.type === 'session.idle' && event.properties.sessionID === runtimeSessionId) {
      onTurnDone();
      stream.close();
    }
  },
});

await session.send('Refactor the auth module');

Ток испоручује догађаје у групама, у размацима од 16 ms. Узастопни догађаји message.part.delta за једно поље дела у групи стижу као један догађај. Његов properties.delta је њихов текст, спојен по редоследу. Његов id је идентификатор последњег догађаја. Његов низ coalesced наводи догађаје које је заменио. Додајте delta на крај поља дела, као за појединачну делту.

Стримовање захтева fetch са стварним телом ReadableStream и TextDecoderStream. Прегледачи, Node 18 и новији, Bun и Cloudflare Workers га подржавају.

Хост чији fetch не може да стримује обезбеђује сопствени транспорт. SDK га позива једном по конекцији и сам задржава управљање поновним повезивањем, наставком преко Last-Event-ID, повећавањем интервала између покушаја и сигналом активности:

import { configureBeeuni, notifyHostSignal, type RuntimeEventTransport } from '@beeuni/sdk';

const transport: RuntimeEventTransport = async function* ({ url, headers, signal }) {
  // Yield { data, id?, retry? } for each server-sent message of ONE connection.
  // Return when the server ends it. Throw (with `status` for a non-2xx answer)
  // when it fails.
};

configureBeeuni({ backendUrl, getToken, eventStreamTransport: transport });

// A host without `visibilitychange` / `online` events reports them itself.
notifyHostSignal('visible'); // the app is back in the foreground
notifyHostSignal('online');  // the network is back
notifyHostSignal('retry');   // a person asked to reconnect now

React Native подразумевано нема fetch са подршком за стримовање. Мобилна апликација Beeuni користи ову тачку интеграције са react-native-sse. createHttpSessionSyncController остаје доступан за хост коме треба само ограничена историја и синхронизација статуса.

Контролер прво учитава најновијих 10 порука. loadOlder() прати серверски курсор. loadHttpSessionHistory() прати сваки курсор за изричите извозе.

Типови догађаја

Сваки догађај има type и објекат properties који садржи његове податке, на пример event.properties.sessionID.

type Када се активира
message.updated / message.removed Порука је промењена или обрисана.
message.part.updated / message.part.removed Део (текст, позив алата, датотека) је проширен или уклоњен.
session.status Промењено је стање заузетости сесије.
session.idle Потез је завршен.
session.error Потез није успео. Догађај садржи грешку.
question.asked Агент је затражио унос.
question.replied / question.rejected Стигао је одговор на питање.

Претворите необрађене поруке и делове у излаз који се може приказати помоћу classifyTurn. Погледајте Референцу за SDK.

Поновни покушај при хладном покретању

ensureReady() проверава крајњу тачку сесије /start — сваки позив чека одговор до 30 s — док извршно окружење не достигне завршну фазу ready/failed/stopped или не истекне рок (readyTimeoutMs, подразумевано ~180 s). У већ покренутој сесији прва провера одмах враћа ready. При хладном покретању провере се настављају док изоловано окружење пријављује retriable: true, па споро покретање само траје дуже, уместо да изазове изузетак. Изузетак ApiError са code: 'RUNTIME_UNAVAILABLE' if the runtime is still not ready избацује се само када рок истекне.

ensureReady() је идемпотентан, па истовремени позиви за исту сесију деле један захтев /start уместо да шаљу више њих. Помоћна функција retryUntilReady испод сада је опциона — ensureReady() већ интерно понавља покушаје — али је и даље корисна ако желите дуже укупно време од подразумеваног readyTimeoutMs.

async function retryUntilReady<T>(ensure: () => Promise<T>): Promise<T> {
  const deadline = Date.now() + 300_000;
  for (;;) {
    try {
      return await ensure();
    } catch (error) {
      const provisioning = error instanceof ApiError && error.code === 'RUNTIME_UNAVAILABLE';
      if (!provisioning || Date.now() > deadline) throw error;
      await new Promise((r) => setTimeout(r, 3_000));
    }
  }
}

Погледајте Класе грешака за пуну структуру ApiError. У React-у, useSession понавља /start уместо вас, па вам овај образац није потребан.

Шта вам говори /start

Сваки одговор /start описује тај позив, а не акумулирану историју реда. То преносе четири поља.

Поље Значење
observed_at Једно време за цео одговор.
action Шта је сервер урадио: inspected, checked_provider, resumed, provisioned, restored, reconciled, awaited_wake, cooling_down.
observation Шта је сервер проверио. known: false значи није проверено у овом позиву — никада „проверено и ништа није пронађено“.
boot phase (provisioning / resuming / booting / ready / parked / failed), since и actively_starting.

boot.actively_starting одговара на питање „да ли је операција добављача тренутно у току за ову сесију?“. Садржај starting са actively_starting: false значи да сервер чека да истекне пауза пре поновног покушаја, а не да се рачунар покреће.

{
  "stage": "starting",
  "retriable": true,
  "reason": "runtime_wake_cooldown",
  "observed_at": "2026-08-26T14:00:00.000Z",
  "action": "cooling_down",
  "boot": { "phase": "resuming", "since": "2026-08-26T13:58:00.000Z", "actively_starting": false },
  "observation": {
    "provider": { "known": false, "status": null, "checked_at": null },
    "runtime": { "known": false, "state": null, "boot_phase": null, "checked_at": null }
  },
  "failure": {
    "category": "sandbox-provider",
    "message": "The runtime did not start (attempt 2). Retrying automatically.",
    "retryable": true,
    "evidence": {
      "check": "provider_not_running",
      "observed_at": "2026-08-26T13:58:00.000Z",
      "error": null,
      "attempts": 2,
      "next_retry_at": "2026-08-26T14:03:00.000Z"
    }
  }
}

Неуспело покретање се аутоматски понавља

Неуспело покретање бележи паузу пре поновног покушаја, а не трајну одлуку. Следећи /start након паузе сам поново покушава буђење. Пауза се продужава са узастопним неуспесима (2 min, 5 min, 10 min). После пет узастопних неуспеха /start враћа stage: "failed" са бројем покушаја у failure.message; та одлука истиче 30 минута након последњег неуспеха, а POST …/restart је одмах поништава.

retriable се изводи при сваком позиву. Стање које сервер још може поново да покуша никада не садржи retriable: false.

failure.evidence наводи проверу која је дала негативан резултат, када је извршена и када сервер поново покушава. Сваки неуспех /start га садржи.

Прилози упита

Отпремите локалне датотеке када их корисници додају у уређивач поруке. Приватне отпремљене датотеке припадају пројекту и не захтевају покренуту сесију.

import { usePromptAttachments } from '@beeuni/sdk/react';

const uploads = usePromptAttachments(projectId);
// File picker, paste, and drop handlers call uploads.add(file) or uploads.addMany(files).
// Render uploads.attachments. Refuse Send only while an attachment is `error` or `aborted`.

async function submit() {
  const ids = uploads.attachments.map((item) => item.id);
  uploads.submit(ids); // Send never waits: the composer clears and uploads continue.
  paintMessage(text, ids);
  try {
    const parts = await uploads.whenReady(ids);
    await beeuni.session(projectId, sessionId).prompts.create({
      clientMessageId,
      messageId,
      parts: [{ type: 'text', text }, ...parts],
    });
    uploads.forget(ids);
  } catch {
    uploads.reclaim(ids);
  }
}

whenReady(ids, { signal }) се разрешава када све отпремљене датотеке буду спремне, редоследом ids. Одбија се када отпремање не успе, буде прекинуто или уклоњено, или када signal прекине операцију. submit(ids) предаје ставке том слању: оне напуштају attachments, престају да се рачунају у ограничења и настављају да се отпремају након демонтирања уређивача поруке. Позовите forget(ids) након успешног POST захтева или reclaim(ids) да их вратите у уређивач поруке након неуспелог слања. Разрешени део датотеке садржи attachment_id, filename и mime. Не садржи URL нити бајтове датотеке. Сервер повезује и материјализује отпремљену датотеку. Свака датотека стиже на рачунар сесије, укључујући слике и PDF-ове: пре извршавања упита, сервер је записује у /workspace/uploads/.beeuni-inbox/, а упит се на њу позива преко путање. Сам упит не преноси бајтове датотеке. Када је чување историје укључено, сервер чува и копију за транскрипт. Неуспело копирање никада не спречава да датотека стигне на рачунар. Датотека коју сервер не може да запише задржава упит, који се поново покушава. Користите ове делове при креирању сесије платформе, покретању већ припремљене сесије или на рутама пријемног сандучета упита. Не прослеђујте приватне идентификаторе директно извршном окружењу runtime sendParts. Стари делови датотека платформе са url и даље раде.

Свака ставка има стабилан id, оригиналну референцу file, receivedBytes и статус: pending, uploading, processing, ready, error или aborted. Послати бајтови одређују напредак. Контролер објављује снимак напретка само при промени целог процента, највише десет пута у секунди по отпремању. processing може остати на 100% док сервер не заврши отпремање. Ограничења су 50 MiB по датотеци, 100 MiB по поруци и 20 датотека. Датотеке не смеју бити празне. Подразумевано се две датотеке отпремају истовремено.

retry(id) наставља са истим идентификатором. После attachment_size_mismatch или attachment_failed сервер не задржава употребљив идентификатор, па retry(id) поново отпрема File као нов прилог. Истекло отпремање не може поново да се покуша: retry(id) избацује изузетак, а грешка ставке садржи код attachment_expired. abort(id) отказује незавршени рад. remove(id) уклања ставку, прекида њено отпремање и одмах се разрешава. Серверски DELETE се извршава по принципу најбољег напора и никада не одбија; отпремљена датотека коју не може да обрише истиче након 24 сата. forget(ids) ослобађа ставке без брисања серверских објеката. Хук прекида наведени рад при демонтирању или промени пројекта и брише своје отпремљене датотеке по принципу најбољег напора: ниједно слање их не задржава, а нацрти не чувају идентификаторе. Предата отпремања се настављају.

Избори постоје само у меморији. Никада не чувајте File објекте, blob URL-ове, потписане URL-ове или идентификаторе отпремања у нацрту. Напуштене отпремљене датотеке истичу након 24 сата.

Окружења без радног оквира користе beeuni.project(projectId).attachments.createController() са истим методама. Претплатите се преко subscribe(listener) и читајте getSnapshot(). Позовите dispose() када се уређивач поруке затвори. Директни позиваоци могу да користе attachments.upload(file, { signal, onProgress, onUpload, resume }) and attachments.delete(attachmentId). Задржите метаподатке onUpload за ручно настављање са истим ID-јем.

Сервер бира транспорт у пољу идентификатора upload:

  • kind: 'direct' је подразумевано. SDK шаље целу датотеку у једном PUT на upload.url са upload.headers и без заглавља Authorization. Уз XMLHttpRequest (прегледачи, React Native), напредак приказује послате бајтове. Уз fetch, напредак приказује 0, а затим пуну величину. Истекли URL или онај који Storage одбије са 400, 401 или 403 поново се потписује једном за исти attachment_id. 409 из Storage значи да је претходни покушај сачувао датотеку.
  • kind: 'chunked' шаље узастопне аутентификоване PUT захтеве од upload.chunk_size бајтова. SDK прихвата сваки позитиван chunk_size. Овај режим бира само окружење чији edge одбацује велика тела захтева.

Завршетак проверава сачувану величину и SHA-256. Ако завршетак одговори са 409 attachment_not_uploaded, onUpload пријављује директну референцу са received_bytes: 0, па retry(id) или наставак поново шаље датотеку. Покретање, отпремање и завршетак понављају покушаје при истеку времена, мрежним грешкама, 429 и 5xx, уз експоненцијално повећање паузе са насумичним одступањем. Временски оквир је 60 секунди од првог неуспеха, па се и дуго отпремање које касно не успе поново покушава. Завршетак такође понавља покушаје за attachment_processing, у оквиру пет минута. Покретање никада не понавља покушај за 402 (BillingError: налог не може да се покрене) или 429 attachment_budget_exceeded (40 незавршених отпремања или 500 MiB непослатих отпремања по кориснику; неискоришћена отпремања истичу у року од 24 сата). Сервер одговара на један захтев за завршетак или га одбија у року од 105 секунди. Сваки захтев за завршетак има рок од 120 секунди. Прекиди које иницира позивалац никада се не понављају.

Референца послатог прилога ослобађа се 1 сат након испоруке његовог упита, као и када се избрише његова сесија или пројекат. Следећи циклус одржавања затим уклања датотеку и њен attachment_id више не може да се пошаље.

Датотеке

s.files чита и пише у изолованом окружењу сесије: list, read, readBlob, status, findFiles, findText, upload, create, copy, remove, mkdir, rename. Сваки позив прво разрешава извршно окружење и увек је усмерен на сопствено изоловано окружење ове сесије. Погледајте SDK референцу за пун списак метода.

Операције сесије

Референца за вас чита из извршног окружења и шаље му одговоре. Свака операција прво припрема сесију и усмерена је на сопствено изоловано окружење ове сесије, без обзира на то који га harness покреће.

// A page of the transcript, oldest first: { info, parts } messages (beeuni.transcript.v1).
const { messages, hasMore } = await s.messages({ limit: 50 });
// What the agent waits on.
const { statuses, permissions, questions } = await s.pending();
await s.answerPermission(permissions[0].id, 'once'); // 'once' | 'always' | 'reject'
await s.answerQuestion(questions[0].id, [['Yes']]); // null dismisses the question
// Summarize the conversation. Needs the `session.compact` capability (see health()).
await s.compact();

s.runtime, клијент за компатибилност са сировим REST-ом, застарео је. Користите горенаведене операције са send, abort, rewind и stream.

Унапред припремите сесију пројекта

Позовите ensureWarm() када је одредишној страници пројекта потребно једно спремно извршно окружење пре првог упита.

const project = beeuni.project(projectId);
const warm = await project.sessions.ensureWarm();

// An ORDINARY session. Prompt it like any other.
await beeuni.session(projectId, warm.session.session_id).send("Build me a widget");

ensureWarm() прави или враћа једну неискоришћену сесију за тренутног корисника. То је исто креирање које извршава sessions.create(), са подразумеваним подешавањима пројекта: истим условом наплате и истим захтевима за конекторе. Једина разлика је metadata.warm, који скрива сесију од sessions.list() док не стигне њен први упит.

Сматрајте то спекулативном припремом. 409 WARM_SESSION_UNAVAILABLE значи да пројекат тренутно не може да се припреми — пређите на sessions.create(), који пријављује стварни разлог.

Унапред припремљена сесија користи DEFAULT агента и изоловано окружење пројекта. Ако корисник изабере друго, напустите је и позовите sessions.create(): неискоришћена унапред припремљена сесија је скривена и аутоматски се уклања.

Обрада грешака

Сваки позив преко createBeeuni одбија се типизираном поткласом Error, никада обичним објектом. Ухватите грешку, проверите instanceof и гранајте логику према .status или .code.

import { ApiError, BillingError } from '@beeuni/sdk';

try {
  await beeuni.project(projectId).sessions.create();
} catch (err) {
  if (err instanceof BillingError) {
    // 402 — out of credits or over a plan limit
  } else if (err instanceof ApiError) {
    // any other failed request — err.status, err.code, err.detail
  } else {
    throw err;
  }
}

Класе грешака

Класа Наслеђује Када се баца Кључна поља
ApiError Error Подразумевана за сваки неуспешан захтев: лош статус, мрежни неуспех, истек времена или прекид status, code, detail, response, url, endpoint, timeout
AuthError ApiError getToken је вратио null. Beeuni није послао захтев code је увек 'NO_SESSION'
BillingError ApiError HTTP 402. Једина класа грешака наплате status (402), code, detail.message
RequestTooLargeError ApiError HTTP 431. Обично превише датотека у једном захтеву detail.suggestion
SessionNotReadyError Error Приступник извршном окружењу покренут је пре ensureReady() name је 'SessionNotReadyError'

ApiError.name је подразумевано 'ApiError'. Два случаја то мењају:

  • name: 'AbortError', code: 'ABORTED' — захтев је отказан, на пример због навигације. То није неуспех. Занемарите га.
  • code: 'TIMEOUT' — истекао је сопствени рок захтева. url, endpoint и timeout показују шта је прекорачило рок.

За сваки други неуспех, status садржи HTTP статусни код. code долази из error_code позадинског система или користи статус као стринг. message је сопствено набројиво својство на ApiError, па се задржава при JSON.stringify и распакивању објекта.

Beeuni још једном шаље захтев који добије 401 са свежим токеном из getToken(), када се тај токен разликује од послатог. Захтев чије је тело једнократни ток не шаље се поново.

Beeuni понавља неке захтеве пре него што ваш код види грешку. Ако захтев GET или HEAD врати 502, 503 или 504, Beeuni га понавља до 2 пута, са паузом од 250ms, а затим 500ms. Привремени транспортни неуспех за GET или HEAD — мрежна грешка, не статусни код — понавља се на исти начин. Успешан поновни покушај никада не стиже до onError. Beeuni никада не понавља захтеве POST, PUT, PATCH или DELETE, нити одговор 500.

Beeuni баца AuthError на клијенту, пре слања захтева, када getToken() врати null. AuthError наслеђује ApiError, па err instanceof ApiError и даље одговара. Проверите err instanceof AuthError или err.code === 'NO_SESSION' да бисте „није пријављен“ обрадили као засебан случај у односу на неуспех позадинског система.

Beeuni баца BillingError за сваки HTTP 402 одговор: потрошени кредити, прекорачено ограничење пакета или други услов наплате. detail.message садржи разлог из позадинског система, а code садржи његов машински код (на пример app_account_unfunded). BillingError наслеђује ApiError, па прво проверите BillingError када обрађујете оба.

Beeuni баца RequestTooLargeError за HTTP 431. То обично значи да је захтев садржао превише датотека. detail.suggestion садржи савет спреман за приказ кориснику.

Грешке спремности сесије

Две грешке значе да изоловано окружење сесије још није спремно. Обрадите сваку другачије.

SessionNotReadyError синхроно баца грешку када позовете приступник за runtime — session.previewUrl() или session.proxyUrl() — пре него што овај handle сесије разреши своје sandbox окружење. Handle сесије разрешава само сопствено sandbox окружење; никада се не враћа на sandbox окружење друге сесије.

import { SessionNotReadyError } from '@beeuni/sdk';

const s = beeuni.session(projectId, sessionId);
try {
  const url = s.previewUrl(3000); // throws: not resolved yet
} catch (err) {
  if (err instanceof SessionNotReadyError) {
    await s.ensureReady();
  }
}

Прво позовите await session.ensureReady() или позовите send(), који интерно припрема сесију. session.health() је једини приступник који никада не баца ову грешку, па можете да га испитујете пре покретања сесије.

RUNTIME_UNAVAILABLE је друга грешка — то значи да је сам ensureReady() истекао чекајући хладно покретање. Погледајте Поновни покушај при хладном покретању за цео образац. У React-у, useSession поново покушава уместо вас и излаже резултат преко вредности phase, уместо да баци грешку.

Помоћне функције

Помоћна функција Потпис Шта ради
parseBillingError(error) (error) => Error Обмотава одговор 402 у BillingError. Остале грешке враћа непромењене
isBillingError(error) (error) => boolean Враћа error instanceof BillingError
formatBillingErrorForUI(error) (error) => BillingErrorUI | null Враћа null за грешке које нису повезане са наплатом. У супротном враћа { alertTitle, alertSubtitle } за модал за надоградњу
isRuntimeNotReadyResponse(error) (error) => boolean Тачно за 503 sandbox daemon-а док runtime сесије још не може да прими захтев. Захтев није прослеђен, па је поновни покушај безбедан. Исти резултат у runtime-у и pi-ју
isSandboxNotReadyError(error) (error) => boolean Тачно за оба претходна одговора и за sandbox који је заустављен, покреће се или је паузиран. Прикажите га као „буђење“ и наставите са испитивањем
isRuntimeStartingError(error) (error) => boolean Тачно за оба претходна одговора и за runtime URL који још није закачен. Error boundary поново покушава у том случају, уместо да прикаже пад
import { formatBillingErrorForUI } from '@beeuni/sdk';

try {
  await beeuni.session(projectId, sessionId).start();
} catch (err) {
  const ui = formatBillingErrorForUI(err);
  if (ui) showUpgradeModal(ui.alertTitle, ui.alertSubtitle);
}

У @beeuni/sdk/react

@beeuni/sdk/react поново извози BillingError, RequestTooLargeError, parseBillingError, isBillingError и formatBillingErrorForUI. Не извози поново ApiError или AuthError — њих увезите из @beeuni/sdk.

useSession класификује сваки неуспех send, answerQuestion, answerPermission и rejectQuestion у један објекат sendError, па не морате ручно да пишете провере instanceof:

interface BeeuniSendError {
  kind: 'billing' | 'runtime-not-ready' | 'runtime-error';
  message: string;
  billing?: BillingError; // set when kind is 'billing'
  cause: unknown;
}
const s = useSession(projectId, sessionId);

if (s.sendError?.kind === 'billing') {
  const ui = formatBillingErrorForUI(s.sendError.billing);
}

Погледајте React hooks за остатак теме useSession.

Да ли вам је ова страница помогла?