Skip to content

Latest commit

 

History

945 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Obversa

Model how your team really works.

license: MIT node >=22.12 TypeScript strict npm: @obversa/runtime CI

Obversa lets you describe how a team works and run it with agents, code and people. One agent writes, another reviews, and a judge decides whether another revision is worth doing. The workflow carries the feedback back to the writer. A person makes the calls you leave to them.

Put that process in a TypeScript file. Use the agent CLIs you already have signed in, or give a role an API engine. Tests run as commands, reviews return their notes, and the run keeps its record in a plain file.

Use the same patterns to review a research brief, shape an article, answer a support ticket or deliver a feature. Notes go back to the writer. A check decides whether work continues. A recorded workflow can continue from its file after an interruption. No server, no database.

Install

npm install @obversa/obversa

Node.js 22.12 or later. @obversa/obversa installs the runtime and every plugin but the Jev engine, which is npm install @obversa/engine-jev-api. Use Claude Code, Codex, Grok or OpenCode with the command line tools you already have signed in, or use an API engine. Installation covers setup. First run walks through a complete file.

Familiar patterns

These are the parts of a process you recognise from working with people. Each snippet comes from a runnable example. Follow its link for the full file, inputs and result.

Get a second opinion

A writer drafts a post and a different model reads it against the house style. The writer gets the review notes and revises the draft. In the editorial example, Claude writes, Codex reviews, and a person is the editor:

    roles: {
      write: engines.claude('claude-sonnet-4-5'),
      grade: [engines.codex('gpt-5.6-luna')],
      editor: person('Publish this post?'),
    },

stage() from @obversa/runtime gives the writer a reviewer and a budget for revisions:

      stage('draft', {
        agent: 'write',
        writes: 'posts/draft.md',
        desc: 'Write the post from the brief, in the house style.',
        gate: 'The draft holds against every rule in style/house.md, as read by a grader from another model family.',
        reviewedBy: 'grade',
        // The judge. After a round the grader did not pass, Jev reads the
        // findings and the rounds so far and says whether another round is
        // worth it; a finding tagged block goes back without asking. The
        // cap is the backstop: three rounds at most, whatever the judge says.
        refine: judge(judgeSeat, { cap: 3 }),
      }),

In workflow(), the reviewer has to come from a different model family from the writer. Get a second opinion explains the review loop.

Know when to stop

A review can suggest changes that make little difference to the result. Give a decision model the job of weighing those notes against the purpose of the work and the revisions so far. judge() from @obversa/runtime sets that stopping rule inside a dag() workflow:

  maxKickbacks: { implement: judge(judgeSeat, { cap: 4 }) },

Here Jev makes the judgement, with a hard cap of four returns to the writer. A blocking finding goes back to the writer without asking the judge, and it still counts toward the cap. Know when to stop shows the questions and a complete writing example.

Ask a panel

Give the work to several reviewers and decide how many must agree. Each opinion stays in the result, including dissent. A workflow() review stage names the panel and the number of approvals it needs:

      stage('review', {
        panel: 'review',
        agree: 1,
        desc: 'Have both reviewers read the change and count the acceptances.',
        gate: 'At least one reviewer has accepted.',
        sendsBackTo: 'implement',
      }),

This example needs one approval from its two reviewers. Set agree to two when both must accept. Ask a panel shows the full team and what happens when a reviewer cannot answer.

Ask a person

A model's review can be enough to request another draft. Publishing may need an editor's decision. The editorial workflow asks the person named in its editor role after the model review passes:

      stage('publish', {
        input: 'editor',
        desc: 'Put the graded draft in front of the editor.',
        gate: 'The editor has said publish.',
        sendsBackTo: 'draft',
      }),

With no answer the run pauses. A refusal with notes goes to the writer. Approval completes this example; a separate action would publish the post. Ask a person shows how the question and answer become part of the run.

Automate the routine work

Let a script check the facts while a model writes the report. The weekly report example uses a command to check that the draft includes every incident. Its exit code decides whether the writer tries again:

      stage('check', {
        run: [process.execPath, 'tools/check-report.mjs'],
        desc: 'Fail the draft if an incident from the facts is missing.',
        gate: 'The check exits 0.',
        sendsBackTo: 'draft',
      }),

The workflow runs the command directly. The model spends its turn on the draft. Automate the routine work shows the same pattern with a test command.

Pick up unfinished work

A weekly report stops after gathering its facts. The next worker opens the saved record and continues with the draft, without gathering those facts again. run() from @obversa/runtime resumes the same workflow:

const second = await run(createReport(), {
  recordTo: record,
  resume: true,
  onEvent: watch('worker-2', worker2),
});

For steps declared with workflow() or dag(), the recovery rule is:

Steps that finished are never repeated. A step that was mid-flight when the worker died runs again only if its binding declares it safe to retry; otherwise the run pauses and asks a person to reconcile it before it continues, so uncertain work is never repeated silently. The complete example shows which stages each worker ran.

The pattern collection also covers comparing different approaches, bringing a team together, and preparing context before handing over a task. Patterns can be combined inside a larger workflow.

Complete workflows

A real use case combines these patterns around an outcome:

Workflow How the team works
Review an article A writer drafts, a different model reviews, and an editor decides whether it is ready to publish.
Read research papers A model summarises supplied papers, a person selects the notes to keep, and those notes inform an answer.
Handle support tickets Classify each ticket, draft a reply, and refer uncertain or sensitive cases to a person.
Prepare a shortlist Apply rules in code, compare rankings from two models, and ask a person to choose the shortlist.
Deliver a feature Plan, implement, test, review and ask a person to approve the change.

The feature delivery file is examples/teams/feature-delivery.ts. Browse the workflows for the full files and examples from other fields.

Engines

A seat names the tool, the provider, the model family and the model.

package drives needs
@obversa/engine-claude-cli Claude Code, one fresh process per attempt Claude Code, signed in
@obversa/engine-codex-cli Codex Codex, signed in
@obversa/engine-grok-cli Grok the Grok command line tool
@obversa/engine-opencode-cli OpenCode the OpenCode command line tool
@obversa/engine-claude-agent-sdk the Claude Agent SDK Claude auth
@obversa/engine-anthropic-api the Anthropic API an API key
@obversa/engine-jev-api Jev, typed decisions over recorded state a Jev endpoint and API key

Write your own against the engine contract in @obversa/api; it must pass the conformance kit.

Where a workflow lives

Keep a workflow in a central collection and point each run at a repository with run(job, { cwd }), keep it beside the code it works on, or import it from a service and call run() when the service decides. It runs the same way from each. Where a workflow lives.

Documentation

docs.obversa.ai: the first run, the concepts, the patterns, examples by field, and how Obversa sits beside LangGraph, CrewAI, Temporal, Claude Code subagents and eve.

Working on Obversa

19 publishable packages. packages/ holds the eight that define the product: @obversa/runtime runs a workflow, @obversa/api holds the engine and memory contracts, @obversa/core runs bounded child processes, @obversa/runner supervises stored runs, @obversa/builtin-workflows ships three ready-made teams, @obversa/obversa is the install above, and @obversa/surface with @obversa/surface-diff is the local review page. plugins/ holds the eleven adapters: the seven engines above, three memories, and a notifier that posts run events to a URL.

AGENTS.md is the guide for anyone who changes this repository: setup, the checks, and the rules every change follows.

License

Obversa uses the MIT License. Report security problems as described in the security policy.

About

Durable workflows for agent teams in TypeScript: named roles, evals that decide the next step, and a person's approval where you want one. Runs Claude Code, Codex and their kin as a team. No server, no database.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages