Back

The Idea#

WishLive was a 48-hour hackathon build to test a specific system shape: agents have registry entries, workflows create work, a runtime executes tool calls, negotiation changes state, settlement requires explicit confirmation, and all of those actions emit typed events.

The project is useful as an architecture experiment, not as evidence of production scale. The counts below describe the implementation and seeded demo data.

48 hHackathon build
6Runtime layers
57Seeded agents
10Wish threshold
WishLive implementation described in this article

Architecture: Six Layers, One Event Bus#

The implementation separates six responsibilities and uses the event bus for cross-layer activity:

flowchart TB
    B[Event Bus] --> C[Concierge]
    B --> S[Settlement]
    B --> N[Negotiation]
    B --> R[Runtime]
    B --> W[Workflow]
    B --> G[Registry]
    U[User Wish] --> W
    G --> W
    W --> N
    N --> R
    R --> S
    C -. observes .-> B
mermaid

Every layer publishes typed events. The event history is the shared record used for tracing cross-layer activity, while each service still owns its local state.


Layer 1: Registry and discovery#

The registry is the DNS + service discovery for agents. Each agent registers with a typed card:

interface AgentCard {
	agent_id: string;
	type: 'musician' | 'venue' | 'manager' | 'audience' | 'business';
	name: string;
	skills: string[];
	reputation: number;
	// ...
}
typescript

Agents send heartbeats every 60 seconds. If they miss, the registry marks them OFFLINE. The discover() method implements an A2A discovery protocol — one agent asks a manager agent to find matching candidates by skill, genre, city, or capacity.

The system seeds 57 agents across 6 types on startup. The onlineCount() method tracks availability by type in real time.

Key runtime behavior: ensureSeeded() auto-seeds on first access, so every service that depends on the registry gets a populated agent network without explicit initialization order.


Layer 2: Wish → Workflow Pipeline#

Users submit wishes: “I want to see Artist X in City Y on Date Z.” The WishWorkflowService:

  1. Aggregates wishes by cohort key (genre + city)
  2. Runs an agent to record the wish processing
  3. When a cohort reaches threshold (10 wishes), triggers demand creation
  4. The matching engine finds candidate musicians and venues from the registry
  5. Publishes demand events that drive the rest of the system

The threshold is a deliberate design choice — it prevents premature negotiation on insufficient demand.


Layer 3: Runtime loop#

This is the core. AgentRuntimeService.run() creates a session, then executes in a loop:

session started → agent thinks → tool execution → message → session completed
plaintext
flowchart LR
    A[Session started] --> T[Agent thinks]
    T --> Q{Tool needed?}
    Q -->|yes| X[Execute tool]
    X --> T
    Q -->|no| M[Emit message]
    M --> D[Session completed]
mermaid

The runtime supports dual-mode execution:

  • Real mode: Uses the Vercel AI SDK with generateText(), passing runtime tools for discovery, pricing, proposals. The agent decides which tools to call based on the user message.
  • Simulated mode: Deterministic fallback when no AI provider is configured. Executes the same tool plan but returns structured mock responses.

The graceful degradation is intentional — simulated mode means I can demo the entire system without an API key. The switch happens transparently at the session level:

if (mode === 'real') {
	try {
		result = await generateWithAISDK(agent, input, session);
	} catch (error) {
		mode = 'simulated'; // graceful fallback
		result = await executePlannedTools(agent, input, session, mode);
	}
} else {
	result = await executePlannedTools(agent, input, session, mode);
}
typescript

Every step publishes to the agent.runtime event stream — thoughts, tool calls, messages, session lifecycle. Langfuse telemetry is wired in for production observability.


Layer 4: Negotiation state#

The negotiation service models multi-agent coordination as explicit, stateful protocols:

create → propose → counter → accept → deal
                    ↓
                 reject
plaintext

Each negotiation has a workflowId, conversationId, and tracks all proposals. The runAutonomousNegotiation() method runs the full pipeline automatically:

stateDiagram-v2
    [*] --> Proposed
    Proposed --> Countered: counter
    Countered --> Proposed: counter
    Proposed --> Accepted: accept
    Countered --> Accepted: accept
    Proposed --> Rejected: reject
    Countered --> Rejected: reject
    Accepted --> Deal
    Deal --> [*]
    Rejected --> [*]
mermaid
  1. Matches top musician and venue candidates from demand
  2. Musician sends initial proposal with terms (venue fee, split percentage)
  3. Venue counters with adjusted terms
  4. Musician accepts
  5. A Deal is created, ready for settlement

The protocol is general enough for human-in-the-loop override — each step has API endpoints for manual accept/reject/counter.


Layer 5: Settlement boundary#

Settlement is where execution becomes real. The system supports dual settlement modes:

On-chain (production):

  • Solidity smart contracts: Escrow (lock/release funds), TicketNFT (mint event tickets), AgentProfile (agent identity)
  • Hardhat deployment to local or testnet
  • Funds held in escrow until deal confirmed

Local (development):

  • Deterministic hash-based transaction IDs
  • localTxHash() generates consistent 0x-prefixed hashes
  • Full simulation of the settlement flow without a blockchain
flowchart LR
    D[Deal accepted] --> E[Create escrow]
    E --> P[Payment or local settlement]
    P --> X{Execution verified?}
    X -->|yes| R[Release settlement]
    X -->|no| H[Hold or dispute]
mermaid

The critical design choice: human confirmation gates. Every settlement action requires explicit confirmation:

async createEscrow(input, options: { confirmed: boolean }) {
  if (!options.confirmed) {
    throw new SettlementError(409, "Human confirmation required");
  }
  // ...
}
typescript

This prevents autonomous agents from moving real money without oversight.


Layer 6: Concierge — The Observability Layer#

The concierge is an LLM-powered interface over the entire runtime. It reads event streams from all layers and answers:

  • “What is happening right now?”
  • “Why is the negotiation stuck?”
  • “What happens next in this workflow?”

It supports both streaming and non-streaming responses, with Langfuse tracking on every interaction. When no AI provider is configured, it falls back to a rule-based simulator that summarizes system state from event data.


The Event Bus: Where Everything Connects#

All subsystems communicate through typed event streams. The bus has two implementations:

  • MemoryEventBus: In-memory event history for development and tests
  • RedisEventBus: Redis Streams implementation for persisted event history

Event types include:

  • agent.lifecycle — registration, heartbeat, offline transitions
  • agent.runtime — session lifecycle, thoughts, tool calls
  • agent.task — A2A message passing between agents
  • negotiation.events — proposals, counters, accepts, rejections
  • settlement.events — escrow creation, fund release, ticket minting
  • wish.events / demand.events / matching.events — workflow pipeline
interface EventEnvelope {
	id: string;
	type: string;
	source: string;
	timestamp: number;
	data: Record<string, unknown>;
	metadata: {
		traceId: string;
		spanId: string;
	};
}
typescript

Every event carries trace context, enabling end-to-end flow reconstruction.


Implementation notes#

The dual-mode runtime was useful because the same workflow could run with an AI provider or with deterministic simulated responses. That made local development possible without an API key. It does not prove that every agent system needs a simulator, but it gave this project a repeatable fallback path.

Typed events made debugging easier because runtime, negotiation, and settlement steps could be reconstructed from one event history. This only works if every state-changing path emits enough information and keeps trace identifiers intact.

Settlement required an explicit confirmation flag before escrow creation or release. That gate is intentionally simple. A real financial workflow would still need stronger authorization, idempotency, policy checks, and recovery handling.

The 57 seeded agents made demos start with usable registry data. They are fixtures, not evidence of marketplace activity.

The Stack#

  • Runtime: TypeScript, Vercel AI SDK, Zod validation
  • Events: Redis Streams (prod), in-memory (dev)
  • Contracts: Solidity, Hardhat, viem
  • Frontend: Next.js 14, Tailwind CSS
  • Observability: Langfuse
  • Infrastructure: Docker Compose, pnpm workspace

Scope#

WishLive is a hackathon prototype for testing event-driven coordination across registry, workflow, runtime, negotiation and settlement layers. The parts worth carrying into another project are the explicit state transitions, typed event envelopes, fallback execution mode and confirmation gate. Production use would still need load testing, stronger authorization, durable recovery tests and deployment-specific monitoring.

The code is at github.com/lora-sys/Hackthon ↗.

End of article · Back to writing

Comments

Comments are public and managed through GitHub Discussions.

Preparing the comment section…

Sign in with GitHub once the comment box is ready, or open the repository discussions directly. Open GitHub Discussions ↗

Comments are published to the public discussions of the lora-sys/loraSys repository.

Other contact methods