Agent Architecture

Purpose and Scope

OpenWiki uses an eve agent as the background execution layer behind the public Next.js application. The app accepts repository generation and repository chat requests, but long-running or streaming work is handed to eve through channels, tools, schedules, and session helpers. This split keeps the web surface responsive while still allowing source-grounded wiki generation, repository-aware chat, and daily refresh automation to share the same authenticated execution model. The repository README describes OpenWiki as a Next.js app backed by an eve agent that plans docs-style outlines, writes source-cited pages, publishes wikis, and keeps repositories fresh; this page maps that product promise to the agent-facing source files.

Sources: agent/agent.ts, agent/channels/index-repository.ts, agent/channels/repo-message.ts, agent/tools/run_index_repository.ts, agent/sandbox/sandbox.ts, agent/schedules/refresh-repositories.ts, agent/lib/run-message.ts

The agent architecture is intentionally small at the top level. The default export in agent/agent.ts calls defineAgent and supplies the configured OpenWiki model through getOpenWikiAgentModel(). That means the entrypoint is not where indexing rules, route validation, or chat behavior live. Instead, the entrypoint establishes the agent runtime model, while feature-specific files define how requests enter the agent, what state they carry, which tools may run, and how callers consume streamed session events.

Sources: agent/agent.ts

Relevant Source Files

  • agent/agent.ts — Defines the eve agent and wires it to the OpenWiki model configuration.
  • agent/channels/index-repository.ts — Exposes the authenticated indexing channel at /eve/v1/openwiki/index-repository, validates index requests, starts task-mode eve sessions, attaches sessions to index jobs, and reports startup failures.
  • agent/channels/repo-message.ts — Exposes the repository chat channel at /eve/v1/openwiki/repo-message, validates chat input and short history, hydrates per-repository workspace state, and starts a repo-message session.
  • agent/tools/run_index_repository.ts — Defines the tool that actually invokes the authorized indexing pipeline from inside an indexing session.
  • agent/sandbox/sandbox.ts — Defines the eve sandbox backend using justbash().
  • agent/schedules/refresh-repositories.ts — Defines the daily repository refresh schedule and home-page revalidation follow-up.
  • agent/lib/run-message.ts — Reads eve run streams, extracts final text replies, captures tool-call lifecycle details, and raises errors for failed or reply-less sessions.

Entry Point, Model, and Runtime Boundary

The agent entrypoint is deliberately declarative: defineAgent({ model: getOpenWikiAgentModel() }). This gives all channels and tools a common model selection without embedding model names in every feature file. In practice, that keeps deployment tuning separate from workflow code. If operators change generation model configuration, the agent still presents the same channel and tool contracts to the web application. For developers, the important boundary is that agent/agent.ts starts the runtime identity, while request validation, job state mutation, and streaming behavior are delegated to channel and library modules.

Sources: agent/agent.ts

OpenWiki treats eve channels as the bridge between HTTP-facing app code and agent sessions. A channel has typed state, a context factory, optional event handlers, and one or more routes. The indexing channel uses defineChannel<IndexAdapterState, IndexChannelContext> with an initial createEmptyIndexAdapterState() and indexChannelEvents. Its route is a POST handler for /eve/v1/openwiki/index-repository. The repository-message channel uses defineChannel<RepoMessageState, { state: RepoMessageState }> with an initial createEmptyRepoMessageState() and a turn.started event that hydrates repository workspace state before the turn proceeds.

Sources: agent/channels/index-repository.ts, agent/channels/repo-message.ts

Channels and Request Contracts

The indexing channel is the server-authorized entrance for wiki generation. Its request schema requires indexJobId, repositoryId, and repoUrl, with an optional webUrl. The handler authenticates the request with authenticateOpenWikiRequest, parses a JSON body, validates it with Zod, and returns a 400 response when the request is not valid JSON or does not match the schema. Once validation succeeds, startIndexRepositoryTask moves the job phase to starting-eve-run, sends an eve task message, records the resulting eve session ID on the index job, and returns the current job state with HTTP 202.

Sources: agent/channels/index-repository.ts

That startup path is designed for reliability rather than fire-and-forget behavior. If an indexing session cannot be started, the channel logs an indexing failure, calls failIndexJob with the error message, and returns a 502 response. If the session starts but the job record can no longer be loaded afterward, the code treats that as an error. This gives the web application and job-status APIs a durable source of truth: the index job remains the user-visible progress record, while the eve session is attached as execution metadata.

Sources: agent/channels/index-repository.ts

The repository-message channel is the chat-oriented counterpart. Its request schema accepts a required message, a required repoUrl, and an optional history array capped at eight assistant or user messages. The route authenticates the request, parses JSON, validates the payload, and calls startRepoMessage with the authenticated result, message, history, repository URL, and eve send function. The route returns the session-start result as JSON with status 202, which matches the asynchronous, session-oriented shape of the indexing channel while supporting a different user workflow.

Sources: agent/channels/repo-message.ts

Tools, State, and Sandbox

OpenWiki’s indexing tool is named by its file, run_index_repository, and its description is explicit about when it may be used: only when the index-repository channel asks the agent to start indexing. The tool has an empty input schema because the authoritative parameters are not supplied by the model at call time. Instead, indexingSessionState.get() must return a state object whose sessionId matches the current eve session. If that guard fails, the tool throws an authorization error rather than trusting model-provided input.

Sources: agent/tools/run_index_repository.ts

After the session-state guard passes, the tool calls runIndexRepositoryJob with indexJobId, repositoryId, repoUrl, and webUrl from the authorized session state. It then reloads the index job. A missing job raises an error, and a job with status failed also raises, using the stored error message when available. On success, the tool returns a compact status object containing the job ID, phase, repository ID, and status. This makes the tool both an execution trigger and a structured checkpoint for the agent run.

Sources: agent/tools/run_index_repository.ts

The sandbox definition is intentionally minimal: defineSandbox({ backend: justbash() }). That tells eve which sandbox backend to use when agent execution needs shell capabilities. The page-generation and indexing implementation can remain focused on repository analysis and publishing while the sandbox file centralizes the backend choice. Because the sandbox is a separate agent module, developers can reason about execution capabilities independently from route contracts and session-state guards.

Sources: agent/sandbox/sandbox.ts

Schedules and Refresh Flow

OpenWiki also uses eve schedules for background maintenance. agent/schedules/refresh-repositories.ts defines a cron schedule of 0 8 * * *, which runs once per day at the configured runtime’s 08:00 schedule boundary. The schedule handler calls waitUntil(runRepositoryRefresh()), so the scheduled event can return control while the refresh task continues in the background. The refresh task calls refreshRepositories(), then attempts to request home-page revalidation through requestHomeRevalidation().

Sources: agent/schedules/refresh-repositories.ts

The scheduled refresh is cautious about follow-up failures. If home-page revalidation fails, it logs a specific error message but does not throw away the refresh result. After refresh execution, the schedule logs operational counters including enqueue limits, featured repository errors, generator enqueue limits, queued repositories, queued generator refreshes, and scanned repositories. This matches OpenWiki’s product goal of keeping published repositories fresh without replacing the last good wiki: the schedule is responsible for discovering and queuing refresh work, while index jobs and generated artifacts remain the durable publication path.

Sources: agent/schedules/refresh-repositories.ts

Shared Message Runner

agent/lib/run-message.ts is the shared reader for eve run streams. It defines readRunMessage for callers that only need the final reply, and readRunMessageWithToolCalls for callers that also need tool-call metadata. The reader consumes a ReadableStream<unknown>, watches event types, remembers the latest message.completed text, and tracks tool calls in a map keyed by call ID. It stops reading when the turn completes, the session waits, or the session completes.

Sources: agent/lib/run-message.ts

The helper also normalizes the tool lifecycle exposed by eve stream events. On actions.requested, it extracts candidate tool-call actions that have a kind or type of tool-call, a string callId, and a string toolName. On action.result, it reads tool-result events, preserves the original input when possible, stores string outputs directly, and JSON-stringifies non-string outputs. If a session.failed event appears, the helper throws with the session failure message. If the stream ends without a completed text reply, it throws RunMessageMissingReplyError.

Sources: agent/lib/run-message.ts

System-to-Code Mapping

Runtime concernSource-level implementationReader takeaway
Agent identity and modelagent/agent.ts uses defineAgent with getOpenWikiAgentModel()Model selection is centralized at the agent entrypoint.
Wiki indexing ingressagent/channels/index-repository.ts defines POST /eve/v1/openwiki/index-repositoryThe web app starts authorized task-mode indexing sessions through a validated channel.
Repository chat ingressagent/channels/repo-message.ts defines POST /eve/v1/openwiki/repo-messageChat requests become eve sessions with bounded history and hydrated repository workspace state.
Index execution toolagent/tools/run_index_repository.ts calls runIndexRepositoryJob after checking session stateThe model cannot invent indexing parameters; they come from authorized session state.
Shell sandbox backendagent/sandbox/sandbox.ts uses justbash()Sandbox capability is configured separately from workflows.
Scheduled maintenanceagent/schedules/refresh-repositories.ts runs daily refresh and revalidationFreshness is an agent-managed operational workflow.
Stream consumptionagent/lib/run-message.ts reads replies, tool calls, and failure eventsCallers get a final text reply plus optional structured tool-call details.

Execution Flow

For repository generation, the web application creates or finds a repository and index job, then calls the indexing eve route with server authentication. The channel validates the payload, marks the job as starting, sends a task-mode message with continuation and state, attaches the eve session to the job, and responds with the current job. Inside that authorized session, the agent can call run_index_repository, which verifies that the current eve session matches stored indexing session state before invoking the indexing engine. Status and errors flow back through the index job record.

Sources: agent/channels/index-repository.ts, agent/tools/run_index_repository.ts

For repository chat, the web application sends a chat payload to the repo-message route. The channel constrains history size, validates roles and message text, authenticates the caller, and starts a repository-message run. On turn start, it hydrates the repository workspace state so the agent can answer against repository context rather than a blank conversation. Consumers that read the resulting stream can use the shared run-message helper to obtain the completed reply and, when needed, observe the tool calls made during the session.

Sources: agent/channels/repo-message.ts, agent/lib/run-message.ts

Next Steps

When changing agent behavior, start by identifying which boundary you are modifying. Model selection belongs near the agent entrypoint. Request shape and authentication belong in channel files. Long-running indexing execution belongs behind the guarded run_index_repository tool. Daily freshness belongs in the schedule. Stream parsing belongs in the shared message runner. For deeper implementation detail, continue with the indexing-engine page for repository analysis and publishing internals, repository-chat for user-facing chat behavior, and keeping-wikis-fresh for refresh policy and stale repository handling.