Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions components/home/CodeTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,16 @@ const checked = validateLoopDefinition(definition)
if (!checked.valid) {
throw new Error(checked.errors.map((e) => e.message).join('; '))
}`,
run: `import { createMemoryLoopStorageAdapter } from '@loop-engine/adapter-memory'
run: `import { memoryStore } from '@loop-engine/adapter-memory'
import { createLoopSystem } from '@loop-engine/sdk'

// assumes \`definition\` from the Define tab
const { engine } = await createLoopSystem({
loops: [definition],
storage: createMemoryLoopStorageAdapter()
store: memoryStore()
})

await engine.startLoop({
await engine.start({
loopId: 'expense.approval',
aggregateId: 'EXP-001',
actor: { type: 'system', id: 'intake' }
Expand All @@ -74,7 +74,7 @@ await engine.transition({
events: `// assumes \`createLoopSystem\` returned \`eventBus\` alongside \`engine\`
const { engine, eventBus } = await createLoopSystem({
loops: [definition],
storage: createMemoryLoopStorageAdapter()
store: memoryStore()
})

eventBus.subscribe(async (event) => {
Expand All @@ -99,7 +99,7 @@ function renderHighlighted(code: string) {
'<span class="kw">$1</span>'
);
return withKeywords.replace(
/\b(parseLoopYaml|validateLoopDefinition|createLoopSystem|createMemoryLoopStorageAdapter)\b/g,
/\b(parseLoopYaml|validateLoopDefinition|createLoopSystem|memoryStore)\b/g,
'<span class="type">$1</span>'
);
}
Expand Down
4 changes: 2 additions & 2 deletions content/docs/ai-and-automation/ai-as-actor.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ The full dual-provider walkthrough is in `/docs/examples/ai-replenishment`, with
| [`@loop-engine/adapter-openai`](/docs/packages/adapter-openai) | OpenAI (`gpt-4o`, o-series) |
| [`@loop-engine/adapter-grok`](/docs/packages/adapter-grok) | Grok (`grok-3`, `grok-2`) via xAI |
| [`@loop-engine/adapter-gemini`](/docs/packages/adapter-gemini) | Gemini (`gemini-1.5-pro`, `gemini-2.0-flash`) |
| [`@loop-engine/adapter-perplexity`](/docs/packages/adapter-perplexity) | Perplexity Sonar (`sonar`, `sonar-pro`, …) — `LLMAdapter` with citations for research steps |
| [`@loop-engine/adapter-perplexity`](/docs/packages/adapter-perplexity) | Perplexity Sonar (`sonar`, `sonar-pro`, …) — `ToolAdapter` with citations for research steps |

The Perplexity package implements `LLMAdapter.invoke()` (text + citations), not the actor `createSubmission` flow. Use it where you need grounded retrieval inside a loop; use the actor adapters above for structured signal decisions.
The Perplexity package implements `ToolAdapter.invoke()` (text + citations), not the actor `createSubmission` flow. Use it where you need grounded retrieval inside a loop; use the actor adapters above for structured signal decisions.

## Related

Expand Down
4 changes: 2 additions & 2 deletions content/docs/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ Versioning: [Semantic Versioning](https://semver.org/)
## Unreleased

### Added
- `@loop-engine/adapter-perplexity` — Perplexity Sonar `LLMAdapter` with citations, retries, and `guardEvidence` integration
- `@loop-engine/core` — `LLMAdapter`, `AdapterInput` / `AdapterOutput`, and deep `guardEvidence()` for adapter audit redaction
- `@loop-engine/adapter-perplexity` — Perplexity Sonar `ToolAdapter` with citations, retries, and `guardEvidence` integration
- `@loop-engine/core` — `ToolAdapter`, `AdapterInput` / `AdapterOutput`, and deep `guardEvidence()` for adapter audit redaction
- Docs: `docs/integrations-perplexity.md` (Sonar vs [Perplexity Computer skills](https://www.perplexity.ai/computer/skills))
- Release contract stub: `.rc/adapter-perplexity.json` (RC **DRAFT** until promoted **LOCKED**)

Expand Down
12 changes: 6 additions & 6 deletions content/docs/examples/postgres-persistence.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,18 @@ QUERY STATE ---> READ INSTANCE + READ HISTORY

```ts
import { createLoopSystem } from "@loop-engine/sdk";
import { createPostgresStore, createSchema } from "@loop-engine/adapter-postgres";
import { createMemoryLoopStorageAdapter } from "@loop-engine/adapter-memory";
import { postgresStore, createSchema } from "@loop-engine/adapter-postgres";
import { memoryStore } from "@loop-engine/adapter-memory";
import { Pool } from "pg";

// Version A: in-memory (default local dev)
const memoryStore = createMemoryLoopStorageAdapter();
const memoryRuntime = await createLoopSystem({ loops: [definition], store: memoryStore });
const memStore = memoryStore();
const memoryRuntime = await createLoopSystem({ loops: [definition], store: memStore });

// Version B: postgres (durable)
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
await createSchema(pool);
const pgStore = createPostgresStore(pool);
const pgStore = postgresStore(pool);
const postgresRuntime = await createLoopSystem({ loops: [definition], store: pgStore });

// Loop definitions + transition calls are unchanged across adapters.
Expand Down Expand Up @@ -79,5 +79,5 @@ pnpm dev
```

<Callout variant="info" title="Adapter pattern">
The same contract pattern extends to `adapter-kafka` and `adapter-http`. Any backend implementing `LoopStorageAdapter` can be used.
The same contract pattern extends to `adapter-kafka` and `adapter-http`. Any backend implementing `LoopStore` can be used.
</Callout>
22 changes: 21 additions & 1 deletion content/docs/getting-started/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ Use at least:

## Install packages individually

If you do not want the SDK aggregate package, install only what you need:
If you do not want the SDK aggregate package, install only what you need.

Core primitives:

- `@loop-engine/core` - domain model types and branded IDs
- `@loop-engine/dsl` - `LoopBuilder`, parser, schema validation
Expand All @@ -50,11 +52,29 @@ If you do not want the SDK aggregate package, install only what you need:
- `@loop-engine/observability` - metrics, timelines, replay
- `@loop-engine/registry-client` - remote/local **loop catalog** client (package name retains `registry-client`)
- `@loop-engine/ui-devtools` - React devtools components

Stores:

- `@loop-engine/adapter-memory` - in-memory `LoopStore`
- `@loop-engine/adapter-postgres` - PostgreSQL `LoopStore` adapter
- `@loop-engine/adapter-kafka` - Kafka `EventBus` adapter
- `@loop-engine/adapter-http` - HTTP webhook `EventBus` adapter

AI adapters:

- `@loop-engine/adapter-anthropic` - Claude actor adapter
- `@loop-engine/adapter-openai` - GPT actor adapter
- `@loop-engine/adapter-gemini` - Gemini actor adapter
- `@loop-engine/adapter-grok` - Grok actor adapter
- `@loop-engine/adapter-perplexity` - Sonar grounded-search adapter

Routing and framework adapters:

- `@loop-engine/adapter-pagerduty` - PagerDuty approval routing
- `@loop-engine/adapter-openclaw` - OpenClaw skill / approval bridge
- `@loop-engine/adapter-vercel-ai` - Vercel AI SDK tool-call governance
- `@loop-engine/adapter-commerce-gateway` - Commerce Gateway tool routing

## Runtime requirements

- Node.js 18+ is required.
Expand Down
9 changes: 4 additions & 5 deletions content/docs/getting-started/quick-start.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,19 @@ eventBus.subscribe(async (event) => console.log(event.type))
await engine.start({
loopId: 'expense.approval',
aggregateId: aggregateId('EXP-2026-001'),
orgId: 'acme',
actor: { type: 'system', id: 'system:intake' }
actor: { type: 'automation', id: 'system:intake', serviceId: 'intake' }
})

await engine.transition({
aggregateId: aggregateId('EXP-2026-001'),
transitionId: transitionId('start_review'),
actor: { type: 'automation', id: 'system:router' }
actor: { type: 'automation', id: 'system:router', serviceId: 'router' }
})

await engine.transition({
aggregateId: aggregateId('EXP-2026-001'),
transitionId: transitionId('approve'),
actor: { type: 'human', id: 'manager@acme.com' },
actor: { type: 'human', id: 'manager@acme.com', userId: 'manager-1', displayName: 'Manager' },
evidence: { comment: 'Approved for Q1 budget' }
})

Expand All @@ -91,7 +90,7 @@ console.log(state?.status) // CLOSED

<Callout variant="info" title="Execution summary">
- The loop moved through 3 states (`SUBMITTED -> UNDER_REVIEW -> APPROVED`)
- 3 actor types were used (`system`, `automation`, `human`)
- 2 actor types were used (`automation`, `human`)
- Each successful transition emitted `loop.transition.executed`
- Reaching a terminal state sets loop status to `CLOSED`
</Callout>
Expand Down
4 changes: 2 additions & 2 deletions content/docs/integrations/http.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ section: Integrations

## What it does

Implements `LoopStorageAdapter` over HTTP. Any REST service that speaks the adapter protocol can back loop state storage. This is useful for integrating with existing services or remote storage planes.
Implements `LoopStore` over HTTP. Any REST service that speaks the adapter protocol can back loop state storage. This is useful for integrating with existing services or remote storage planes.

## Adapter protocol

Expand Down Expand Up @@ -51,7 +51,7 @@ Expected backend endpoints:

## Note

If your backend is in the same process, implementing `LoopStorageAdapter` directly is usually simpler than standing up the HTTP protocol.
If your backend is in the same process, implementing `LoopStore` directly is usually simpler than standing up the HTTP protocol.

## Configuration reference

Expand Down
4 changes: 2 additions & 2 deletions content/docs/integrations/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Connect Loop Engine to any AI provider, agentic platform, storage backend, or ob

## ⟩ AI Providers

Use any major LLM as a governed actor. Provider actor adapters produce the same `AIAgentActor` shape — switching providers requires changing one import. Perplexity Sonar covers **grounded retrieval with citations** as an `LLMAdapter` for research steps (see the package reference).
Use any major LLM as a governed actor. Provider actor adapters produce the same `AIAgentActor` shape — switching providers requires changing one import. Perplexity Sonar covers **grounded retrieval with citations** as a `ToolAdapter` for research steps (see the package reference).

<div className="grid gap-4 md:grid-cols-2">
<IntegrationCard
Expand Down Expand Up @@ -136,7 +136,7 @@ Connect Loop Engine to commerce platforms and LLM commerce data layers.

## Building an integration?

The Loop Engine adapter interface is open and documented. Any backend that implements `LoopStorageAdapter` is valid.
The Loop Engine adapter interface is open and documented. Any backend that implements `LoopStore` is valid.

- [Adapter interface docs →](/docs/running-loops/adapters)
- [Apply for certification →](mailto:oss@betterdata.co)
Expand Down
2 changes: 1 addition & 1 deletion content/docs/integrations/openclaw.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ This is the Sense → Decide → Govern pattern in practice:

| Option | Type | Default | Description |
|---|---|---|---|
| `loopSystem` | `LoopSystem` | required | Runtime instance used for governed transitions |
| `loopSystem` | `LoopEngine` | required | Runtime instance used for governed transitions |
| `signalMap` | `Record<string, string>` | `{}` | Optional action-to-signal mapping override |
| `defaultActor` | `Actor` | `ai-agent/openclaw` | Fallback actor attribution metadata |
| `onProposal` | `(proposal) => Promise<void>` | — | Optional hook before transition submission |
Expand Down
2 changes: 1 addition & 1 deletion content/docs/integrations/perplexity.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ section: Integrations

## What it does

`@loop-engine/adapter-perplexity` implements `LLMAdapter.invoke()` against the Sonar chat API. You get answer text plus structured citations for audit and evidence attachment. Use it where provenance matters more than open-ended generation.
`@loop-engine/adapter-perplexity` implements `ToolAdapter.invoke()` against the Sonar chat API. You get answer text plus structured citations for audit and evidence attachment. Use it where provenance matters more than open-ended generation.

## When to use it

Expand Down
2 changes: 1 addition & 1 deletion content/docs/integrations/postgres.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ section: Integrations

## What it does

Swaps the in-memory adapter for PostgreSQL persistence. Loop state, transitions, and events are stored in Postgres through the same `LoopStorageAdapter` interface, so loop definitions and business logic remain unchanged.
Swaps the in-memory adapter for PostgreSQL persistence. Loop state, transitions, and events are stored in Postgres through the same `LoopStore` interface, so loop definitions and business logic remain unchanged.

## Quick setup

Expand Down
22 changes: 9 additions & 13 deletions content/docs/packages/actors.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,24 @@ npm install @loop-engine/actors
```ts
interface HumanActor extends ActorRef {
type: "human"
sessionId: string
userId: string
displayName: string
roles?: string[]
}

interface AutomationActor extends ActorRef {
type: "automation"
serviceId: string
version?: string
}

interface AIAgentActor extends ActorRef {
type: "ai-agent"
agentId: string
gatewaySessionId: string
recommendedBy?: string
}

interface WebhookActor extends ActorRef {
type: "webhook"
source: string
}

interface SystemActor extends ActorRef {
type: "system"
modelId: string
provider: string
confidence?: number
promptHash?: string
toolsUsed?: string[]
}
```

Expand Down
2 changes: 1 addition & 1 deletion content/docs/packages/adapter-perplexity.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ section: "packages"

## Overview

`@loop-engine/adapter-perplexity` wraps the Perplexity Sonar chat API as a Loop Engine `LLMAdapter`. Sonar adds grounded web retrieval with cited sources. You use it for Loop steps that need real-time, verifiable information — regulatory lookups, compliance research, supplier or market news. It is not a general-purpose generation adapter; for broad LLM actor flows, use [Anthropic](/docs/packages/adapter-anthropic) or [OpenAI](/docs/packages/adapter-openai) actor adapters.
`@loop-engine/adapter-perplexity` wraps the Perplexity Sonar chat API as a Loop Engine `ToolAdapter`. Sonar adds grounded web retrieval with cited sources. You use it for Loop steps that need real-time, verifiable information — regulatory lookups, compliance research, supplier or market news. It is not a general-purpose generation adapter; for broad LLM actor flows, use [Anthropic](/docs/packages/adapter-anthropic) or [OpenAI](/docs/packages/adapter-openai) actor adapters.

## Installation

Expand Down
3 changes: 1 addition & 2 deletions content/docs/packages/core.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,11 @@ export interface ActorRef {
export interface LoopInstance {
loopId: LoopId
aggregateId: AggregateId
orgId: string
currentState: StateId
status: LoopStatus
startedAt: string
closedAt?: string
correlationId: CorrelationId
correlationId?: CorrelationId
metadata?: Record<string, unknown>
}

Expand Down
29 changes: 23 additions & 6 deletions content/docs/packages/dsl.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Builder methods implemented in source:
- `.build(): LoopDefinition`

<Callout variant="warning" title="Current builder surface">
`guard()`, `signal()`, and `actor()` are not LoopBuilder methods in `v0.1.0`. Guards are added inside `.transition({ guards: [...] })`.
`guard()`, `signal()`, and `actor()` are slated for `1.1.0+` as experimental builder methods and are not implemented in the current release. Guards are added today inside `.transition({ guards: [...] })`.
</Callout>

## Fluent example
Expand Down Expand Up @@ -128,24 +128,41 @@ See `/docs/defining-loops/yaml-format` for the full format reference.
```ts
import {
parseLoopJson,
parseLoopFile,
serializeToJson,
serializeToYaml,
parseLoopYaml,
serializeLoopJson,
serializeLoopYaml,
validateLoopDefinition
} from "@loop-engine/dsl"
```

Validation signature:

```ts
validateLoopDefinition(input: unknown): { valid: boolean; errors: string[]; definition?: LoopDefinition }
validateLoopDefinition(definition: LoopDefinition): ValidationResult

interface ValidationResult {
valid: boolean
errors: ValidationError[]
}

interface ValidationError {
code: string
message: string
path?: string
}
```

Validation failure example:

```ts
{
valid: false,
errors: ["initialState: initialState must exist in states"]
errors: [
{
code: "INVALID_INITIAL_STATE",
message: 'initialState "DRAFT" does not exist in states',
path: "initialState"
}
]
}
```
20 changes: 10 additions & 10 deletions content/docs/packages/events.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,28 @@ npm install @loop-engine/events

## Event catalog

The exported `LOOP_EVENT_TYPES` constants map to:
The exported `LOOP_EVENT_TYPES` constant enumerates the nine canonical lifecycle events:

- `loop.started`
- `loop.completed`
- `loop.cancelled`
- `loop.failed`
- `loop.transition.requested`
- `loop.transition.executed`
- `loop.transition.blocked`
- `loop.guard.failed`
- `loop.completed`
- `loop.error`
- `loop.spawned`
- `loop.signal.received`
- `loop.outcome.recorded`

Every event extends:

```ts
interface LoopEventBase {
eventId: string
type: string
loopId: LoopId
aggregateId: AggregateId
orgId: string
occurredAt: string
correlationId: CorrelationId
correlationId?: string
causationId?: string
}
```
Expand Down Expand Up @@ -70,9 +69,10 @@ unsubscribe()
```ts
extractLearningSignal(
completed: LoopCompletedEvent,
history: TransitionRecord[],
predicted?: Record<string, unknown>
history: LoopTransitionExecutedEvent[],
definition: LoopDefinitionLike,
predicted?: Record<string, number>
): LearningSignal
```

This helper derives `actual`, `predicted`, and numeric `delta` fields from completion history.
The helper derives `actual`, `predicted`, and numeric `delta` fields from the completed event, the executed-transition history, and the loop definition's declared business metrics. `predicted` keys not declared in `definition.outcome.businessMetrics` are dropped with a warning.
Loading