Skip to content

piotr-mamenas/orenda

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

15 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Orenda β€” Agentural Framework for Reflective Workspace Monitoring (MIT)

Orenda is an open-source, YAML-driven agent framework for continuously scanning collaboration workspaces (e.g., Slack public channels) and sending soft, reflective messages to users. Instead of micromanaging, Orenda encourages self-correction and non-invasive orchestration: the system detects issues (like unanswered questions or risky tone) and nudges people to reflect in their own context and language.

The name Orenda comes from the Iroquois concept of a spiritual force that empowers change. This framework is a β€œmirror” that helps teams notice, reflect, and improveβ€”on their own.


✨ What Orenda Does

  • Continuously scans channels (e.g., Slack public channels).
  • Runs Detectors (LLM or rule-based) that spot conditions (unanswered questions, tone risks, confusion/blockers, etc.).
  • Triggers Reflectors that deliver gentle messages at the right time (immediately, later in the morning, next Monday).
  • Supports refire and escalation logic for persistent issuesβ€”without becoming intrusive.
  • Is fully declarative via YAML: define logic as nodes, link prompts by file path, schedule everything, and modularize with includes.
  • Ships with JSON Schemas for validation and a CLI for lint/compile/run.

🧠 Philosophy: Non-Invasive Management

Orenda is a self-management amplifier. It promotes:

  • Autonomy: People decide if/how to act after reflection.
  • Clarity: Detectors are explicit, finite, and auditable.
  • Respect: Reflectors are phrased in the user’s language and tone, with actionable options.
  • Safety: PII redaction and clear audit trails.

πŸ—οΈ Architecture & Execution Flow

Orenda implements a sophisticated execution engine with the following components:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Scheduler  │───▢│  Dispatcher  │───▢│  NodeRuntime    β”‚
β”‚ (Cron Jobs) β”‚    β”‚ (Orchestra.) β”‚    β”‚ (Executes LLM)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚                      β”‚
                           β–Ό                      β–Ό
                   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                   β”‚    Router    β”‚    β”‚ EscalationHandlerβ”‚
                   β”‚ (Next Nodes) β”‚    β”‚  & DeliveryServiceβ”‚
                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚                      β”‚
                           β–Ό                      β–Ό
                   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                   β”‚ StateManager β”‚    β”‚  SlackService   β”‚
                   β”‚ (PostgreSQL) β”‚    β”‚ (Messaging API) β”‚
                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Core Components

🎯 NodeRuntime - Executes individual detector/reflector nodes

  • Runs LLM prompts with context injection
  • Handles binary and categorical detection modes
  • Manages escalation and refire conditions
  • Stores execution results for audit trails

🧭 Router - Handles graph traversal and next node resolution

  • Compiles YAML trees into optimized runtime format
  • Resolves next nodes based on detector results
  • Prevents infinite loops with cycle detection
  • Validates tree structure and dependencies

🚦 Dispatcher - Orchestrates execution across multiple threads

  • Queue management with priority and retry logic
  • Concurrent execution with configurable limits
  • Integration with all core services
  • Run tracking and progress monitoring

⬆️ EscalationHandler - Manages escalation logic and refire states

  • Multi-target escalation (users, channels, managers, on-call)
  • Cooldown periods to prevent spam
  • Escalation rule management and tracking
  • Integration with organizational systems

πŸ“¬ DeliveryService - Handles scheduled message delivery

  • Scheduled delivery with retry logic
  • Multiple delivery targets (thread, DM, channel)
  • Persistent job storage and recovery
  • Cleanup of completed jobs

⏰ Scheduler - Cron-based execution triggering

  • YAML tree scheduling with cron expressions
  • Auto-sync and tree recompilation
  • Manual trigger capabilities
  • Slack message synchronization

πŸ”§ Supporting Infrastructure

  • StateManager: PostgreSQL-based message and context storage
  • SlackService: Slack API integration for messaging
  • LLMRunner: OpenAI GPT integration with context injection
  • Merger & RolesResolver: YAML configuration processing
  • Validator: JSON Schema validation

Execution Flow

  1. πŸ“… Scheduler triggers trees based on cron expressions
  2. πŸ”— Merger processes YAML configuration with includes & roles
  3. 🧭 Router compiles tree into optimized execution format
  4. 🚦 Dispatcher queues root detector nodes for each relevant thread
  5. 🎯 NodeRuntime executes detectors with LLM integration
  6. 🧭 Router determines next nodes based on results
  7. ⬆️ EscalationHandler manages escalations when limits reached
  8. πŸ“¬ DeliveryService schedules reflector messages for delivery
  9. πŸ’Ύ StateManager persists all execution state and results

🏁 Quick Start

1) Install

git clone https://github.com/your-org/orenda.git
cd orenda

# Node or Deno or Bun; choose your runtime. Example with Node:
npm install

2) Setup PostgreSQL Database

Orenda requires PostgreSQL for storing messages, contexts, and execution state.

# Install PostgreSQL (if not already installed)
# On macOS: brew install postgresql
# On Ubuntu: sudo apt-get install postgresql postgresql-contrib
# On Windows: Download from https://www.postgresql.org/download/

# Create database and user
psql -U postgres
CREATE DATABASE orenda_dev;
CREATE USER orenda WITH ENCRYPTED PASSWORD 'your_password';
GRANT ALL PRIVILEGES ON DATABASE orenda_dev TO orenda;
\q

Update src/config/settings.dev.json with your database connection:

{
  "connectionString": "postgresql://orenda:your_password@localhost:5432/orenda_dev",
  "slack": {
    "botToken": "xoxb-your-slack-bot-token"
  },
  "openai": {
    "apiKey": "sk-your-openai-api-key"
  }
}

3) Define your tree & roles

Create or edit:

  • orenda-tree.yaml β€” the one canonical tree for your app.
  • orenda-roles.yaml β€” maps symbolic roles (e.g., @teamlead) to users.

Example minimal tree:

version: 1.5
tree:
  id: orenda_main
  name: "Orenda Daily Channel Scan"
  schedule: "0 3 * * *"   # run daily 03:00
  include:
    - trees/detectors/unanswered.yaml
    - trees/reflectors/unanswered.yaml

Example roles:

version: 1.0
roles:
  "@teamlead": "john.doe"
  "@user": "{{thread.owner}}"

3) Add prompts

Create prompt files under prompts/ and reference them by path in nodes.

Example prompts/reflect_unanswered.md:

You are a reflector node.
Notify the user they likely missed a reply.

Context:
- thread.url
- question_excerpt
- asker_display

Message:
You may have missed a question from {{asker_display}} in {{thread.url}}:
> {{question_excerpt}}

Please reply or acknowledge today.

4) Validate

# Validate YAML against schemas
orenda lint --schema schemas/orenda_tree.schema.json orenda-tree.yaml
orenda lint --schema schemas/orenda_roles.schema.json orenda-roles.yaml

5) Dry run

# Simulate on sample data (no messages posted)
orenda run --dry --tree orenda-tree.yaml --since P1D

6) Go live

# Start scheduler (executes tree cron and reflector deliveries)
orenda start

🧩 Orenda YAML Language β€” Complete Rules

Orenda’s YAML is small but powerful. It describes one application tree plus modular includes, detectors and reflectors, and the timing semantics that make it humane.

0) One-Tree Rule

  • Each Orenda app exposes one canonical root file: orenda-tree.yaml.
  • That file may include other YAML fragments to keep things modular.
  • The final compiled graph is a single DAG (directed acyclic graph).

1) Root Structure

version: <string|number>

tree:
  id: <string>                 # a-zA-Z0-9_.-
  name: <string>
  schedule: <cron>             # when the tree runs (e.g., "0 3 * * *")
  include:                     # optional; merges external YAMLs
    - trees/detectors/unanswered.yaml
    - trees/reflectors/unanswered.yaml
  nodes:                       # optional in root; often defined in includes
    - { ...node definitions... }

Notes:

  • schedule on the root defines the main daily/periodic run (e.g., process last day’s threads).
  • include merges external YAML files before validation and execution.

2) Nodes

Two node types: detector and reflector.

Detector Node

- id: detect_unanswered
  type: detector
  mode: binary | categorical
  prompt: prompts/detect_unanswered.md        # external file path
  triggers:                                   # mapping of outputs β†’ next node
    on_true: reflect_unanswered               # for binary
    # or for categorical:
    # risky_tone: reflect_tone
    # neutral_only: end  (implicit termination if not mapped)
  refire:                                     # optional: re-detection logic
    delay: "48h"                              # OR schedule: "0 10 * * MON"
    prompt: prompts/detect_unanswered_refire.md
    limit: 2                                  # max rechecks
  escalation:                                 # optional
    when: limit_reached | persistent_true | repeated_offense
    next: reflect_escalate_manager
  • Detectors are parallel: they run independently on the same batch.

  • Mode:

    • binary: emits true/false β†’ use triggers.on_true.
    • categorical: emits one of a finite set β†’ map each category to a next node via triggers.
  • Refire:

    • delay (e.g., "15m", "2h", "1d") or schedule (cron).
    • prompt overrides instruction for the refire phase.
    • limit caps rechecks to prevent loops.
  • Escalation:

    • when:

      • limit_reached: fires when refire.limit is exhausted.
      • persistent_true: issue stayed true across consecutive runs (engine tracks).
      • repeated_offense: same user/thread triggered >N times in a window (configurable; tracked by engine).
    • next: reflector to trigger when escalation condition hits.

Reflector Node

- id: reflect_unanswered
  type: reflector
  prompt: prompts/reflect_unanswered.md
  to: "@teamlead"                       # optional; resolve via roles
  delivery:
    delay: "15m"                        # OR schedule: "0 9 * * *"
  end: true                             # terminal node
  • Reflectors are terminal: every path must end in at least one reflector with end: true.

  • Delivery:

    • delay: relative timing after detection (s/m/h/d suffix).
    • schedule: cron timing (e.g., batch to 09:00 daily).
  • to:

    • Role symbol (e.g., @teamlead) or leave empty to default to thread owner; roles resolved via orenda-roles.yaml.

Important: Prompts are referenced by path, not embedded. This keeps YAML clean and prompts reusable.

3) Triggers vs Outputs

  • Use triggers to map detector outputs to next nodes.
  • outputs is a legacy synonym and should be avoided going forward.

4) Includes (Modular Trees)

  • include accepts a list of YAML fragment files.

  • Included files typically expose:

    nodes:
      - id: ...
        type: detector|reflector
        ...
  • Node IDs must be globally unique after merging.

  • Include depth is supported (recommend ≀3).

  • The root file remains the source of truth for schedule and app identity.

5) Roles Mapping (orenda-roles.yaml)

version: 1.0
roles:
  "@teamlead": "john.doe"                           # static
  "@user": "{{thread.owner}}"                       # dynamic template
  "@manager":
    primary: "{{users[thread.owner].manager}}"      # fallback chain
    fallback: "@cto"
  • Role names start with @.

  • Values can be:

    • Static usernames (platform handles).
    • Templated expressions evaluated at runtime.
    • Objects with primary + fallback.
  • Reflectors use to: "@role" and are resolved at delivery time.

6) Validation Rules (Exhaustive)

  • One root tree per app (orenda-tree.yaml).
  • All includes must merge into a DAG without cycles.
  • Every branch must terminate in a reflector (end: true).
  • First runnable nodes are detectors (reflectors are terminal and do not start branches).
  • detector.mode is binary or categorical only; categories are finite.
  • triggers targets must reference valid node IDs.
  • delivery in reflectors must specify delay or schedule (not both).
  • refire in detectors must specify delay or schedule (not both); limit β‰₯ 1.
  • escalation.when ∈ {limit_reached, persistent_true, repeated_offense}.
  • Role symbols used in to: must exist in orenda-roles.yaml.
  • Prompts are file paths (Markdown or text), not inline strings.

🧱 Repository Structure

orenda/
β”œβ”€ README.md
β”œβ”€ LICENSE
β”œβ”€ .gitignore
β”œβ”€ .eslintrc.json                   # ESLint configuration
β”œβ”€ package.json                     # npm dependencies and scripts
β”œβ”€ tsconfig.json                    # TypeScript compiler configuration
β”œβ”€ examples/                        # example configurations and templates
β”‚  β”œβ”€ orenda-example-tree.yaml      # example root tree configuration
β”‚  β”œβ”€ orenda-example-roles.yaml     # example role mappings
β”‚  β”œβ”€ detectors/
β”‚  β”‚  β”œβ”€ unanswered.detector.yaml
β”‚  β”‚  β”œβ”€ tone.detector.yaml
β”‚  β”‚  └─ blockers.detector.yaml
β”‚  β”œβ”€ reflectors/
β”‚  β”‚  β”œβ”€ unanswered.reflector.yaml
β”‚  β”‚  β”œβ”€ tone.reflector.yaml
β”‚  β”‚  └─ escalation.reflector.yaml
β”‚  └─ prompts/
β”‚     β”œβ”€ detect_unanswered.prompt.md
β”‚     β”œβ”€ detect_unanswered_refire.prompt.md
β”‚     └─ detect_tone.prompt.md
β”œβ”€ schemas/
β”‚  β”œβ”€ orenda_tree.schema.json       # JSON Schema for tree configurations
β”‚  β”œβ”€ orenda_roles.schema.json      # JSON Schema for role mappings
β”‚  └─ orenda_nodes.schema.json      # JSON Schema for node definitions
β”œβ”€ src/
β”‚  β”œβ”€ cli/
β”‚  β”‚  └─ main.ts                    # CLI entry point
β”‚  β”œβ”€ config/
β”‚  β”‚  └─ settings.dev.json          # development settings
β”‚  β”œβ”€ loader/
β”‚  β”‚  β”œβ”€ yaml-loader.ts             # reads tree & includes
β”‚  β”‚  β”œβ”€ merger.ts                  # merges nodes, resolves paths
β”‚  β”‚  β”œβ”€ validator.ts               # JSON-schema validation + graph checks
β”‚  β”‚  └─ roles-resolver.ts          # resolves @roles β†’ users
β”‚  β”œβ”€ runtime/
β”‚  β”‚  β”œβ”€ scheduler.ts               # cron for tree + deliveries + refires
β”‚  β”‚  β”œβ”€ dispatcher.ts              # per-thread fanout, concurrency, retries
β”‚  β”‚  β”œβ”€ node-runtime.ts            # executes detectors/reflectors
β”‚  β”‚  β”œβ”€ delivery-service.ts        # posts messages (e.g., Slack)
β”‚  β”‚  β”œβ”€ escalation-handler.ts      # limit/persistence/offense handling
β”‚  β”‚  └─ state-manager.ts           # persistence for refires/escalations
β”‚  └─ integrations/
β”‚     β”œβ”€ llm-runner.ts              # LLM integration for prompt execution
β”‚     └─ slack-service.ts           # Slack API integration
└─ tests/ (empty)                   # test files and fixtures

πŸ§ͺ Examples

Example: trees/detectors/unanswered.yaml

nodes:
  - id: detect_unanswered
    type: detector
    mode: binary
    prompt: prompts/detect_unanswered.md
    triggers:
      on_true: reflect_unanswered
    refire:
      delay: "48h"
      prompt: prompts/detect_unanswered_refire.md
      limit: 2
    escalation:
      when: "limit_reached"
      next: reflect_escalate_manager

Example: trees/reflectors/unanswered.yaml

nodes:
  - id: reflect_unanswered
    type: reflector
    prompt: prompts/reflect_unanswered.md
    delivery:
      delay: "15m"
    end: true

  - id: reflect_escalate_manager
    type: reflector
    prompt: prompts/reflect_escalate_manager.md
    to: "@teamlead"
    delivery:
      schedule: "0 9 * * *"
    end: true

πŸ”’ Governance & Safety

  • PII Awareness: redact sensitive content before LLM calls where appropriate.
  • Audit Trail: every node execution, reflection, and escalation is logged with timestamps and resolved recipients.
  • Access Control: restrict who can modify trees/prompts.
  • Cost Visibility: record tokens/costs per detector to budget LLM usage.

πŸ›  CLI (Conceptual)

# Lint YAML using schemas
orenda lint --schema schemas/orenda_tree.schema.json orenda-tree.yaml

# Compile includes β†’ flattened JSON DAG (for inspection)
orenda compile --tree orenda-tree.yaml --out build/compiled_tree.json

# Dry run (no messages posted)
orenda run --dry --tree orenda-tree.yaml --since P1D

# Start scheduler + delivery services
orenda start

πŸ“œ License (MIT)

MIT License

Copyright (c) 2025 ...

Permission is hereby granted, free of charge, to any person obtaining a copy
...

(See LICENSE for full text.)


🀝 Contributing

  • Fork, create a feature branch, and open a PR.
  • Add/update unit tests and schemas.
  • Keep YAML language minimal & declarativeβ€”new features must not introduce scripting.
  • Provide examples and docs for any new node properties.

πŸ—Ί Roadmap (Indicative)

  • Resolution listeners (auto-cancel refires when the thread is answered).
  • Multi-workspace ingestion & cross-channel deduplication.
  • Local rule engine for common detectors (zero-LLM).
  • Visual DAG editor for non-technical users.
  • More delivery adapters (email, Teams, Discord).

Orenda is an invitation to reflectβ€”quietly and effectively. Define your detectors, schedule your reflections, and let your teams self-optimize.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published