Repository navigation
Agent kit core
Pure Dart (no Flutter, no runtime dependencies): the view models of an
agent chat, the AgentBackend port, a pure reducer that folds events into a
snapshot, typed command errors and a scripted fake backend. Its JSON shapes
are the Dart mirror of the SDK's layer 2 client contract; see
backend-mapping.md.
-
package:agent_kit_core/agent_kit_core.dart: models, items, events, snapshot, reducer, backend port, errors,ChatFeed. -
package:agent_kit_core/fake.dart:ScriptedBackend,ManualClock,SystemClockand the "Quota bar flickers on reconnect" script.
loadChat(id) returns a ChatSnapshot up to sequence N;
events(chat, after: N) streams KitEvents, each {seq, chat, kind, at?, ...}.
reduce(snapshot, event) returns the next snapshot:
- events for another chat and unknown kinds change nothing;
- durable events with
seq <= snapshot.seqare dropped; - transient events (
message.delta,thinking.delta,tool.output,job.output) carry the last durable seq, are dropped when older than the snapshot, and apply by characteroffset, so a repeated fragment is appended once; -
gapmarks the snapshotstale; the client reloads.
A completed message replaces its streamed item. run.finished closes
streaming items; running tool rows become cancelled, or done when the run
completed. Rewind and fork reach a client as a gap and a reload.
ChatFeed does the loop: load, subscribe, reload on gap (buffering events
that arrive meanwhile), and mark the snapshot stale when the stream ends.
Immutable classes with value equality (defined by their JSON) and
toJson/fromJson; sealed classes for variants (TranscriptItem,
ActivityRow, KitEvent, KitError). Markdown is an opaque string and
references are KitRef spans {kind, id, label, start, end}. Workspace
panes are not part of core.
- Transcript items: user, assistant, system, notice, activity group (thinking, tool and spawn rows), file edit, child result, compaction marker, child question, interrupted.
- Panels: approvals, questions, queue, plan, children, jobs, usage, workspace, run, context.
-
Status is derived, never stored:
needsYou(pending approval or question), thenrunning(active run, quota wait included, or a running child; jobs do not count), thenfailed/interrupted/donefrom the last run, elseidle. - Questions are requests of 1 to 5 questions (see below).
-
Approvals have
requestedAtand no deadline. After a host restart they come backexpiredwith reasonrestart, and the runinterrupted, which offersresume.
ChatSnapshot.context is a ContextView?, the serving view of the model's
window, replaced whole by a context.changed event. Everything a host does
not record is null and left out of the JSON; a widget hides a null, never
shows zero.
-
ContextView {window?, applyCap, responseReserve?, blocks, pendingEpisodeIds}with the sum getterstotalTokens,openingTokens,memoryTokensandliveTokens(null when a block lacks its tokens). -
ContextBlock {kind, label, tokens?, firstSeq?, lastSeq?, summaryId?};ContextBlockKindisopening,memoryIndex(the chapter index;indexis taken by Dart's enum),anchors,chapterSummary,episodeSummary,rawSuffix. -
ContextDetailsis whatloadContextreturns:chapters(ContextChapter),episodes(ContextEpisode),summaries(ContextSummary, text possibly empty untilloadSummary) andevents(ContextEvent), with lookupschapter(id),episode(id),summary(id). -
ContextEvent {id, kind, at, firstSeq, lastSeq, tokensBefore?, tokensAfter?, summaryId?, trigger?, triggerText?, inputBefore?, inputAfter?};ContextEventKindisepisodeWritten,chapterClosed,applied;ContextTriggerispressure,emergency,sourceSize,subjectChange,manual,unknown(an unrecognised wire value). -
HistoryPage {firstSeq, lastSeq, messages}ofHistoryMessage {seq, role, preview, tokens?}, for the windowed history list. -
CompactionItem {eventId?, state, episodesSummarized?, tokensBefore?, tokensAfter?, detail?}is the marker in the transcript.compaction.updatedupdates the one item with thatitemIdin place, keeping its position and first time;stateisrunning,doneorfailed. -
InterruptedItem {at, reason?}(eventinterrupted.added) is the line a run interrupted by a restart leaves; therun.outcomefallback stays only for snapshots without it. -
Question.answeredAtis set fromquestion.resolved.
Port: loadContext, loadHistoryRange(chatId, from, to) and loadSummary,
all behind KitCapability.context.
A child agent can ask its parent a question. It is not the user's question:
the child is WorkState.waitingForAnswer, and the reducer keeps it out of
needsYou, the pending-question dock and any "waiting for you" count. The
WorkState values also include idle and timedOut; WorkStateX.isEnded
is true for done, failed, cancelled and timed out.
-
ChildQuestion {id, childId, text, options, askedAt, state, answer?, answeredAt?, answeredBy?};stateispending,answeredorcancelled,answeredByisparentoruser. -
ChildAgent.pendingQuestionholds the open one; the transcript gets aChildQuestionItem {question, task}with idchildq:<questionId>, updated in place when answered or cancelled.ChatSnapshot.pendingChildQuestionslists the open ones. - Events:
child.question.asked(the child waits, a row is added) andchild.question.resolved(answer,answeredBy,at; the child runs again). A child that ends cancels its open question. A second answer, or one for an unknown question, changes nothing. Progress cannot revive a waiting child. - Port:
answerChildQuestion(chatId, childId, questionId, text)behindKitCapability.answerChildQuestion. It is the user answering in the parent's place, recorded asansweredBy: user.
A Question is one request from the agent: {id, requestedAt, items, state, answers?, reason?}. It holds 1 to maxQuestionItems (5) QuestionItems, and
the constructor (so fromJson too) throws ArgumentError for an empty or
longer list, or for a pick item with no options and no allowOther. Hosts
must split anything over 5 into several requests.
-
QuestionItem {id, header, text, kind, options, allowOther}:headeris the short tab label,textis markdown.kindis aQuestionKind:pickOne(one option, or typed Other text),pickAny(any number, including none, plus Other text) ortext(a typed answer only;optionsis empty andallowOtheris ignored). Kinds can vary within a request. -
QuestionOption {label, description?}. -
QuestionAnswer {choices, text?, skipped}:choicesare option labels,textis the Other text or the answer to atextitem, andskippedmarks a declined item. ForpickOne, at most one ofchoicesandtextis set. -
Question.answersmaps item ids to answers once the request is answered. The port'sanswerQuestion(chatId, questionId, answers)takes the same map and every item gets an entry (a skipped item hasskipped: true); the fake backend rejects a map with a missing item asKitInvalid. - Events keep their shape:
question.askedcarries theQuestion;question.resolvedcarriesquestionId,stateandanswers. - Like approvals, questions never expire on their own;
requestedAtgives the age ("waiting 12m").
mixedQuestionScript() (in fake.dart) ends a chat waiting on a request of
a pick-one item with descriptions and Other, a pick-any item and a text item.
The widgets are QuestionPrompt and QuestionAnswerLines in agent_kit's
composer folder.
ChatReader supplies snapshots, events and capabilities to ChatFeed without
requiring commands. AgentBackend adds commands. KitCapabilities declares
supported operations; absent command capabilities fail with KitUnsupported.
BackendConstraints describes reference, question-answer and running follow-up
limits, with defaults preserving existing backend behavior.
Commands return futures and fail with a KitError subclass whose message
is safe to show: KitUnsupported, KitNotFound, KitBusy, KitConflict,
KitInvalid, KitUnavailable, KitDenied, KitNeedsSetup, KitFailed.
KitNeedsSetup means the backend cannot do this until something is configured (a model provider, say); its message is the backend's own text and says what, and a retry works after the setup.
Any KitError has hostCode and hostData, null unless the backend returned a typed error; KitInvalid and KitFailed can carry them (a BubblePaw config.set refusal: hostCode is the JSON-RPC code, hostData the host's data, message the host's text).
Besides the reads, the port has createChat, send (starts a run or
queues), steer, followup, sendQueuedNow, editQueued, removeQueued,
cancel, resume, decideApproval, answerQuestion, answerChildQuestion, rename, archive,
fork, rewind, cancelChild, cancelJob, undoEdit, listModels,
listProfiles, setModel, setProfile, listProviders and
setDefaultModel (capabilities providers and defaultModel; a
ProviderSetup carries whether a key was found, never a key), and the context reads
loadContext, loadHistoryRange, loadSummary.
ScriptedBackend plays ChatScripts (timed steps of event JSON) on a
KitClock, folds every event into its own state and answers commands with
the events a real backend would send. Sequence numbers are global, as in the
SDK's store. Steps $disconnect and $reconnect drop and restore delivery;
reconnecting sends a gap. failNext makes the next command fail;
commands records every call. With ManualClock, playback is stepped by
advance or runAll and is deterministic.
-
test/golden_test.dartreplays eachtest/fixtures/*.jsonland compares the snapshot with<name>.json. The format is a shared contract, written up intest/fixtures/README.md, so the SDK's layer 2 can replay the same files.UPDATE_GOLDENS=1 dart testrewrites the expected files. -
test/question_test.dart: the question model (every kind round-trips, 0 and 6 items and an option-less pick item are rejected) and the mixed question scenario withanswerQuestion. -
test/backend_test.dart: the clock, the fake backend's commands and errors, and the quota scenario end to end through a reconnect, checking the final snapshot holds every element of the mockup. -
test/context_test.dart: the context models, the marker updated in place,interrupted.added,Question.answeredAtand the three context reads. -
test/child_question_test.dart: the child-question model, the reducer flow (a child question never makes the chatneedsYou),answerChildQuestionand the quota scenario's two child questions. - The quota scenario also carries the LCM history (two chapters, four episodes, one applied compaction, one ready summary, 48.6k of 128k with an 84k cap), the compaction marker and the two child questions (one answered by the parent, one left pending).
Generated from docs/agent-kit-core.md. Edit the file there; changes made on the wiki are overwritten.
Guides
- Package layout and pinning
- agent_kit_core
- Backend mapping
- agent_kit and the gallery
- Icon and README assets
Component sheets
theme
status
transcript
- ActivityGroupView
- AssistantProse
- ChildQuestionRow
- CodeBlock
- DiffView
- ExecOutput
- FileEditRow
- PlanChecklist
- ReturnLine
- SystemLineView
- TranscriptList
- UserBubble
composer
chrome
context
history
outline
workspace
compositions
providers