Rna Agent
Rna SDK

Initiative and learning

InitiativeCoordinator lets a project choose its own actions without a goal; LearningCoordinator manages reflection and experiments.

InitiativeCoordinator

The coordinator takes project observations and calls a real model to choose among wait, work, share and learn. The host supplies a handler for each action and persistence.

import { InitiativeCoordinator } from './packages/sdk/src/index.mjs';

const initiative = new InitiativeCoordinator({
  project,
  model,
  apiKey,
  provider,
  handlers, // host implementations of work / share / learn
  policy: {
    tokenBudget: 200000,
    decisionMaxTokens: 2048,
    workBudgetTokens: 24000,
    maxConcurrent: 1,
    cooldownMs: 10000,
  },
  saveState: (state) => writeJsonAtomically(statePath, state),
  onEvent: emit,
});

await initiative.observe({
  id: 'project-opened:1',
  type: 'project_opened',
  summary: 'The user just created the project and has not asked for anything yet.',
});

const result = await initiative.tick({ presence: { typing: false, activeTurn: false }, signal });
await initiative.idle();

writeJsonAtomically, handlers and emit belong to the host. Pass the real input state and active turn as presence: it only changes when to speak, not the background judgment.

A complete runnable example:

node packages/sdk/examples/proactive-project.mjs \
  --model ./model.json \
  --title "Offline backup tool" \
  --description "Verifiable, recoverable backups for indie developers" \
  --workspace ./packages/sdk --interactive

Stopping repeat work

The host can supply a workCheck hook, called after the model decides work and before it is queued. Covered work is not queued and takes the duplicate path, with review backing off; lastDecision, the result and the event carry admission, and the next decision sees previousDecision at the end of its request. A failing hook lets the work through. The Rna host wires it to JEV’s work.coverage judgment.

Other host hooks

  • messageCheck(decision): a host check of the text of share and ask; when it returns violations the model is asked to rewrite.
  • decisionReasoning(input): the host picks the reasoning effort for this decision by returning { reasoning }; returning nothing or throwing keeps the model's own setting.
  • policy.notificationMuted: mute. Shares at the inbox and digest tiers go through; inline and notify shares and questions are held.

Long-running

  • decisionTimeoutMs, actionTimeoutMs and separate failure backoff.
  • archiveHistory({ actions, seenEventIds }) must succeed before hot history is trimmed; without an archiver nothing is deleted.
  • Custom functions that ignore cancellation are isolated; unknown side effects stay needs_review instead of being “recovered” by retries.

LearningCoordinator

The host provides saveState, review and execute, and feeds lightweight episodes with stable identities through ingest(). tick() persists its claim before calling the model.

Every reflection must choose one outcome:

OutcomeMeaning
no_changeNothing should change under current evidence
deferPostponed with a due time
experimentA falsifiable hypothesis, queued as a frozen experiment

Repeated failures on the same evidence back off and enter awaiting_evidence after the third; only genuinely accepted new external evidence lifts it, never a repeated ingest or timer. After a restart, unfinished reviews and experiments are marked interrupted; unknown experiments are never replayed automatically.

EvolutionCoordinator

Freezes the project baseline, calls the host evaluator and runs baseline and candidate on the same cases. Only when improvement, safety, cost and no-regression gates all pass does it emit evolution_candidate_ready. Trial, promotion and rollback are explicit host calls; a candidate model’s own score never counts as evaluation.

The Rna app now defaults to gene-tree evolution: one gene at a time, verified by real work. Paired evaluation of whole candidates remains as an optional deep check.

On this page