Skip to content

Repository files navigation

Rebis o-[]-o

(~ solve (x) '(-> ,x "Verify the answer"))

(["Ship one result"] (solve "Find the bug") (solve "Design the fix"))

In two forms this defines a reusable agent graph, fires two instances, routes each real answer through verification, and mediates both verified results into the program's returned value.

Rebis is a pure S-expression language for programming LLM-agent systems. Quoted strings are raw prompts, bare atoms are Lisp-like symbols, ~ defines structural macro abstractions, arrows route actual answers, and squares contain executable mediator code.

program := expr+ EOF
expr := prompt | symbol | '\'' expr | ',' expr | '(' form ')'
form := '~' symbol '(' symbol* ')' expr
      | '#' module | '<' expr '>' expr+
      | '$' expr+
      | '&' symbol expr+
      | '^' expr
      | '%' expr expr expr
      | '->' expr expr+ | '<-' expr expr+
      | symbol expr* | expr+

As in Lisp source files, a program may contain multiple top-level forms without an extra pair of parentheses. They share one lexical definition scope and execute in source order; the parser retains that boundary as an implicit program node.

The central form is ([M] A B ...). Branches run first; their actual answers become RESULT 1, RESULT 2, and so on in source order, then enter mediator program M. A host may evaluate mutually isolated branches concurrently while preserving that source-ordered join.

Parallel square hosts

orchestrate_parallel evaluates every eligible [] child concurrently, bounded by RuntimeLimits::with_max_concurrency. orchestrate_parallel_with_inlet adds a thread-shareable input seam. Groups, arrows, gates, and the mediator remain sequential; branch answers, events, and diagnostics rejoin in source order.

An Oracle may override begin_parallel, try_fire_scoped, and finish_parallel. Those lifecycle callbacks give hosts a stable ExecutionScope for branch-local resources and put reconciliation immediately before the mediator. Returning Ok(false) from begin_parallel safely falls that square back to ordinary sequential execution. Rebis itself remains zero-dependency and filesystem-free; a host can supply containers, copies, Git worktrees, or no external isolation at all.

Lazy control is explicit: (% condition when-one when-zero) evaluates the condition first, requires exactly 1 or 0, and evaluates only the chosen continuation. A square is never conditional; [] always mediates all of its branches. The embedded std-binary protocol uses $ to ask for one exact decision token before the gate validates it.

([(->
    "Compare all reports"
    "Resolve disagreements"
    "Write one verified fix")]
  "Inspect the code and reproduce the failure"
  "Trace the execution and find the root cause")

Macro abstractions use (~ name (parameter ...) body) and ordinary Lisp-style calls:

(
  (~ inspect (target)
    (-> target "Write a detailed report"))
  (inspect "Inspect the parser"))

Application substitutes argument expressions structurally. It never changes text inside quoted prompts. Only quoted prompts fire agents.

Composition and variables

The string is the language's fundamental value. ($ A B ...) is the one operator that transforms it: it interpolates its operands into one string and yields that string — pure text construction, nothing inside $ fires or runs. An operand contributes its text: a prompt its characters, a symbol its bound value, a macro its expanded text (not fired), a nested $ its assembled text. The assembled string is a prompt in the position the $ sits, so it fires there, once. Because it is an operator, not in-string interpolation, it never peeks inside a quoted string — "it cost $100" stays literal.

Variables are macro parameters. A macro binds names; a call supplies the values, which $ weaves in as text — reused freely, with no extra model call per use:

(~ case (self rival)
  ($ "Make the strongest case that " self " beats " rival " in hip-hop."))

(["Deliver the verdict: who has the greater hip-hop legacy, and why?"]
  (case "Jay-Z" "Kanye West")
  (case "Kanye West" "Jay-Z"))

This fires exactly two advocates and one mediator: self and rival are text, so reusing them does not multiply model calls.

A text constant is just a macro whose body is a prompt — $ interpolates its text without firing it, so no quoting is needed:

(~ topic () "the fall of Rome")
($ "Write a short explainer on " (topic) ".")
; one model call — (topic) is woven in as text, not fired on its own.

To carry a model-computed value into a prompt, use -> (it flows the answer in as INPUT:); $ builds text, -> carries results.

Input ports

(& port body) receives an external input under the name port, then runs body with that name bound to whatever the host supplies:

(& input (-> input "Summarize this"))
; `input` yields the host value and flows into the prompt as its INPUT:

The host decides what fills the port — a prior run's answer, another running agent's output, a line typed at a terminal. Using the port name where a value flows (an arrow producer, a $ operand) yields that value; no model fires for the port itself. The core stays pure: the value is fixed for the run. A host may block inside its input seam until the value arrives, so & is the language's "stop until it receives input" — the building block for one agent feeding another and for long-lived, supervised agents.

Syntax inversion

(^ E) takes the orientation dual of Rebis syntax without calling a model. It recursively exchanges -> and <-, preserves written operand order, and leaves prompts, symbols, imports, and the complete contents of $ fixed:

(^ (-> "gather evidence" (<- "write report" "challenge evidence")))
; becomes (<- "gather evidence" (-> "write report" "challenge evidence"))

It is an involution: (^ (^ E)) is exactly E. Groups, squares, and quotes retain their shape while their children are dualized. Macro definitions stay fixed; a call expands first and its resulting graph is inverted, so a local definition and its call are dualized exactly once.

Macros are higher-order: a named macro symbol can be passed as an argument and substituted in call-head position.

(
  (~ apply (worker target) (worker target))
  (~ inspect (target) (-> target "Write a report"))
  (apply inspect "Inspect the parser"))

Repeated parameters may duplicate model work, so hosts should enforce expanded call, size, token, and time budgets.

For explicit Scheme/Common-Lisp-style construction, quote holds output syntax and comma splices caller syntax:

(~ twice (work) '(-> ,work ,work))

Macros may call themselves. The % binary gate evaluates its condition as an exact 1/0 decision and runs only the selected branch. This provides loops without a separate recursion operator; the reference runtime caps expansion at 256 macro calls.

Modules, abstraction, and expansion

(# module) imports the top-level macro definitions of a host-resolved Rebis module. Kaos resolves modules from saved hypersigils in ~/.kaos/sigils; names may be qualified, such as std/loops. The crate embeds sixteen modules under that reserved namespace: import one leaf with (# std/loops), or the complete folder with (# std). Hosts may apply the same definition-only expansion to their own folders; Kaos recursively imports a saved folder such as (# team). Module bodies are definition-only and may re-export other modules with #.

For example, save this as the hypersigil engineering.rebis:

(
  (~ investigate (issue)
    '(->
      ,issue
      "Reproduce the failure"
      (["Choose the strongest causal explanation"]
        (<- "Challenge the trace" "Trace state backward from the symptom")
        (-> "Inspect the relevant code" "Propose the earliest wrong state"))))

  (~ repair (issue)
    '(->
      (investigate ,issue)
      "Implement the smallest root-cause fix"
      (<- "Return the reviewed patch" "Run tests and search for regressions"))))

Then a program can import and expand it twice:

(
  (# engineering)

  ([(->
      "Compare both repairs"
      "Resolve contradictory evidence"
      "Write the final design and patch plan")]
    (repair "Tower middleware lacks ad-hoc span metadata")
    (repair "Future cancellation can lose the closing event")))

The compact call:

(repair "Future cancellation can lose the closing event")

structurally expands into the full nested ->, <-, mediator, and investigate graph from the module. Arguments remain syntax throughout the expansion; no prompt text is interpolated or reparsed.

kaos rebis run --allow-tools examples/incident.rebis
kaos rebis run --dry '(["Combine reports"] "Inspect code" "Trace failure")'
kaos rebis tree '(["synthesize"] "Inspect code" "Trace failure")'

Kaos provides a direct editor plus an optional Vim-like mode with normal/insert/visual modes and % bracket matching. ctrl / opens Kaos commands (/run, /tree, /mandala, /format, /panel, /graph); : remains reserved for Vim file commands. See the specification, guide, symbol reference, standard library, and host notes.

About

No description, website, or topics provided.

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages