Skip to content
Open
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
1 change: 1 addition & 0 deletions docs/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

* xref:architecture.adoc[Architecture Overview]
* xref:composition.adoc[Composition]
* xref:console.adoc[Console]
* xref:memory.adoc[Memory]
* xref:storage.adoc[Storage]
* xref:skills.adoc[Skills]
Expand Down
21 changes: 11 additions & 10 deletions docs/modules/ROOT/pages/composition.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ Qlawkus lets a single manifest, `agent.yml`, decide which capabilities are compo

[NOTE]
====
The manifest schema, the generator (`mvn qlawkus:generate`), the capability catalog, the `runtime` toggle tier, and dev-mode reload are all in place: the generator reconciles the pom against the capabilities the reactor's extensions declare, unknown names fail the build, the resolved set is reported, and the `runtime` block is applied at boot with an external override. Composing a capability in or out is a build-time concern by design (a rebuild, the same closed-world trade a native image makes); the `runtime` section is the part that changes without one.
Composing a capability in or out is a build-time concern by design: a rebuild, the same closed-world trade a native image makes. The `runtime` section is the only part that changes without one.
====

== Why a generator

Quarkus resolves Maven dependencies *before* augmentation runs, and Maven does no tree-shaking, so a build step cannot add or drop dependencies for its own build. The only place a manifest can decide the dependencies is a step that runs *ahead* of the build. Qlawkus does this with a Maven plugin, the same idea as `quarkus extension add`:

[source,text]
----
agent.yml -> mvn qlawkus:generate -> pom.xml -> mvn package (or quarkus build --native)
----
++++
<div class="mermaid">
graph LR
M["agent.yml<br/>manifest"] -->|"mvn qlawkus:generate"| P["pom.xml"]
P -->|"mvn package / quarkus build --native"| A["agent artifact"]
</div>
++++

A deselected capability is left out of the pom entirely, so the result is lean by construction - not by relying on native-image dead-code elimination.

Expand Down Expand Up @@ -140,23 +143,21 @@ All three require authentication. The staged manifest lives in the app's own sta

== The configuration editor

The manifest above decides *which capabilities* exist. The same admin surface also lets you edit *any documented property value* - `qlawkus.*` in full, plus a small curated allowlist of `quarkus.*` properties - from `/console/config` (when the optional `console` capability is composed in) or directly over the API. The form is generated from the same `quarkus-config-doc` metadata that builds xref:config-reference.adoc[the config reference], so there is no separate list to keep in sync, and secret properties (API keys, password hashes, tokens) never appear here - they stay in xref:secrets.adoc[the keystore].
The manifest above decides *which capabilities* exist. The same admin surface also lets you edit *any documented property value* - `qlawkus.*` in full, plus a small curated allowlist of `quarkus.*` properties - over the API, or from the xref:console.adoc[console] at `/console/config` when the optional `console` capability is composed in. The form is generated from the same `quarkus-config-doc` metadata that builds xref:config-reference.adoc[the config reference], so there is no separate list to keep in sync, and secret properties (API keys, password hashes, tokens) never appear here - they stay in xref:secrets.adoc[the keystore].

Each property is edited through one of two tiers, depending on its `ConfigPhase`:

[cols="1,1,3"]
|===
|Phase |Applies |Endpoint

|`RUN_TIME` |Live, on the next restart |`PUT`/`DELETE /api/admin/runtime-toggles` - writes straight into the runtime-toggle override file described above.
|`RUN_TIME` |On the next restart |`PUT`/`DELETE /api/admin/runtime-toggles` - writes straight into the runtime-toggle override file described above.
|`BUILD_TIME` / `BUILD_AND_RUN_TIME_FIXED` |Next rebuild |`PUT`/`POST`/`DELETE /api/admin/config-overrides` - stages a `.properties` document, the same stage-then-promote pattern as the manifest.
|===

A `BUILD_TIME` write is validated (documented, in-scope, not a secret) and staged only - never applied directly - exactly like the manifest. The staged document lives in its own file, `config-overrides.properties`, parallel to `agent.yml` rather than merged into it, so promoting it can overwrite the file wholesale without ever touching a hand-written line in `application.properties`.

== The schedule page

`/console/schedule` lists the agent's five background jobs - memory review, memory curation, episodic consolidation, skill curation, and skill lifecycle - sourced from the live Quarkus `Scheduler` rather than a hand-maintained list, so cron and previous/next fire time always match what is actually registered. Editing a job's cron reuses the same `RUN_TIME` tier as the configuration editor above (`PUT`/`DELETE /api/admin/runtime-toggles`); triggering a job immediately hits its existing manual-trigger endpoint (`POST /api/admin/memory/{review,curate,consolidate}`, `POST /api/admin/skills/{curate,lifecycle}`).
The console surfaces the same two tiers as a form, and adds a xref:console.adoc#_management_pages[schedule page] that drives the background jobs. Both are just front-ends over these endpoints.

== The restart contract

Expand Down
51 changes: 51 additions & 0 deletions docs/modules/ROOT/pages/console.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
= Console
:page-title: Console
:page-description: The optional server-rendered admin UI - first-run setup plus memory, skills, cognition, config and schedule management.

The console is an optional server-rendered admin UI at `/console`. It is a first-run setup wizard and a set of management pages over the agent's existing admin API - memory, skills, cognition, configuration, and the background schedule. It is built with Qute templates and https://htmx.org[HTMX], so it needs no Node build and no separate frontend.

The console never re-implements admin logic. Its read views inject the same SPIs the agent uses (`FactStore`, `SkillStore`, the live Quarkus `Scheduler`, the config-metadata index); every mutating action is driven from the page by HTMX against the existing `/api/admin/*` endpoints. So the UI and the API can never drift, and anything the console does is equally scriptable with `curl`.

== Enabling and reaching it

The console is the composition capability `console`. It ships in the reference `app` (it is in the baked `agent.yml`), so a stock build already serves it; a slimmed distribution can leave it out (see xref:composition.adoc[Composition]). When present, browse to `/console` - `http://localhost:8080/console` in dev mode, or `http://localhost:8742/console` for the containerized run.

Every console page is `@Authenticated` and carries no login of its own: it reuses whatever mechanism the app already enforces on `/api/admin/*`. In the bundled distribution that is HTTP Basic over an Argon2id credential (`qlawkus` / `dev` in dev mode; see xref:secrets.adoc[Secrets]). Add `quarkus-oidc` to the app and the console is behind SSO with no console-side change.

== First-run setup wizard

On first boot the agent cannot yet talk to an LLM, so the Overview shows a setup banner linking to the wizard at `/console/setup`. The wizard writes nothing itself: it orchestrates the same primitives documented elsewhere - the keystore writer (secrets, applied on restart) and the composition admin service (a staged manifest, applied on the next rebuild).

It is split into two phases by the closed-world boundary, because some steps only make sense once a capability has actually been compiled in:

[cols="1,3",options="header"]
|===
|Phase |Steps

|*A - before a rebuild* |The LLM base URL, chat model and API key (saved to the keystore), and the capability selection (staged as an `agent.yml` manifest). These are all you need to get the agent talking and to choose what to build next.
|*B - after a rebuild* |The interactive steps a capability unlocks once it is baked in: a bot token for each messaging adapter that is present, and Google authorization for the Google Workspace tools. Each Phase B step is shown only for a capability the running app already carries.
|===

So a typical bring-up is: run the wizard's Phase A, rebuild and redeploy with the staged manifest (see xref:composition.adoc#_the_restart_contract[the restart contract]), then return to the wizard for Phase B on the capabilities you turned on.

== Management pages

[cols="1,3",options="header"]
|===
|Page |What it does

|*Overview* (`/console`) |A live status panel - app version, the baked composition posture and capability set, and a server clock that ticks on every HTMX poll to show the refresh is live. Hosts the first-run setup banner.
|*Memory* (`/console/memory`) |The most recent facts, the fact sources in use, and the episodic journals. Review, curate and purge are HTMX calls to `/api/admin/memory/*`. See xref:memory.adoc[Memory].
|*Skills* (`/console/skills`) |The current skill index. Pin, delete, curate and the lifecycle sweep call `/api/admin/skills/*`. See xref:skills.adoc[Skills].
|*Cognition* (`/console/cognition`) |Reconcile and migrate between the markdown and pgvector representations, over `/api/admin/cognition/*`. Shown only when the `cognition.pgvector` capability is baked in (the console does not depend on that extension - it reads the manifest to decide). See xref:storage.adoc[Storage].
|*Config* (`/console/config`) |The full configuration editor: one section per documented config root, generated from the same `quarkus-config-doc` metadata that builds xref:config-reference.adoc[the config reference], never a hand-maintained list. Each property shows its live effective value and source; secrets never appear. Saving routes by config phase - `RUN_TIME` properties to `/api/admin/runtime-toggles` (apply on restart), `BUILD_TIME` ones to `/api/admin/config-overrides` (apply on the next rebuild). See xref:composition.adoc#_the_configuration_editor[Composition > the configuration editor].
|*Schedule* (`/console/schedule`) |The five background jobs (memory review, memory curation, episodic consolidation, skill curation, skill lifecycle), sourced from the live Quarkus `Scheduler` so cron and previous/next fire times always match what is actually registered. Editing a cron reuses the `RUN_TIME` runtime-toggle path; triggering a job now hits its manual `/api/admin/*` endpoint.
|===

A note the Config and Schedule pages surface: when a property's effective value already comes from a source that outranks the runtime-toggle override file (an environment variable, `.env`, or a `-D` flag), the page warns that saving there will not take effect until that higher-priority source is removed - so a silent no-op is caught in the UI rather than by trial and error.

== What's next?

* xref:composition.adoc[Composition] - the manifest the setup wizard stages, and the rebuild contract that applies it
* xref:secrets.adoc[Secrets] - the Argon2id credential the console authenticates against, and the keystore the wizard writes to
* xref:config-reference.adoc[Configuration Reference] - every property the config editor can render
31 changes: 26 additions & 5 deletions docs/modules/ROOT/pages/google-workspace.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -77,23 +77,44 @@ export QLAWKUS_GOOGLE_STORAGE_ENABLED=true

== 6. Credential Vault (Optional but Recommended)

By default, Qlawkus stores OAuth tokens in an encrypted PostgreSQL table using AES-256 + Argon2id. Enable with:
The credential vault persists the Google refresh token so authorization survives restarts. It is off by default in the bundled `app`; enable it with a passphrase:

[source,bash]
----
export QLAWKUS_VAULT_ENABLED=true
export QLAWKUS_VAULT_PASSPHRASE=your-strong-passphrase
----

If disabled or no passphrase provided, tokens are kept in memory and lost on restart.
When enabled with a passphrase, the refresh token is encrypted with AES-256-GCM (the key is derived from the passphrase via Argon2id) and stored in the `qlawkus_google_auth` datasource. If the vault is disabled or no passphrase is set, tokens are kept in memory and lost on restart. Treat the passphrase as a secret: see xref:secrets.adoc[Secrets] to store it in the keystore under `qlawkus.google.vault.encryption-passphrase` instead of a plain environment variable.

== 7. First Authorization Flow

Authorization uses the OAuth 2.0 authorization-code (loopback) flow. The agent never sees your Google password: it hands you a consent URL, and Google redirects a one-time code back to the callback, which the agent exchanges for a refresh token.

1. Start the agent (dev mode or Docker)
2. Send a message that requires Google access (e.g., "List my emails")
3. The agent will return an authorization URL
4. Open the URL in a browser, sign in with your Google account, and grant permissions
5. You'll be redirected back to the agent, which stores the tokens
3. The agent calls its `startGoogleAuthorization` tool and returns an authorization URL
4. Open the URL in a browser, sign in with your Google account, and grant the requested scopes
5. Google redirects to `/api/google/oauth/callback`; the agent exchanges the code for a refresh token, stores it (see the vault above), and confirms via `checkGoogleAuthorization`
6. Ask again - the agent retries the original request automatically

++++
<div class="mermaid">
sequenceDiagram
participant U as You
participant A as Agent
participant G as Google
U->>A: "List my emails"
A-->>U: authorization URL (startGoogleAuthorization)
U->>G: open URL, sign in, grant scopes
G-->>A: redirect to /api/google/oauth/callback?code
A->>G: exchange code for refresh token
A->>A: store refresh token in the vault
A-->>U: authorized - retry your request
</div>
++++

The xref:console.adoc[Console] onboarding wizard can run this same authorization from a web page instead of chat.

== Available tools

Expand Down
15 changes: 8 additions & 7 deletions docs/modules/ROOT/pages/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,11 @@ Inspired by projects like https://github.com/openclaw/openclaw[OpenClaw] and htt

Most personal AI agents store memory as markdown files on disk. The model decides when to read them, which means it can forget to check. Qlawkus takes a different approach: **memory is injected into the prompt automatically, so it never depends on the model deciding to look - and the agent can still search it on demand** when it needs to dig deeper.

Every turn, the agent receives:
Every turn, the agent receives its own `Soul` (identity, mood, behavioral bias) and a curated `UserProfile` of the person it serves, plus the facts relevant to the current message - retrieved by a vector search that runs **before each reply**, not by a tool the model has to remember to call.

* A `Soul` - the agent's own identity, mood, and behavioral bias
* A `UserProfile` - a coherent, curated record of the human it serves
Facts are tagged by source (`semantic-extractor`, `remember-tool`, `episodic-consolidator`, `transcript`) and kept clean by background jobs: nightly deduplication merges near-duplicates, and a curation job distills scattered facts into the single `UserProfile`. Transcripts are embedded too, so the agent can semantically search what was actually said.

Below the surface, a https://github.com/pgvector/pgvector[pgvector]-backed fact store holds granular memories tagged by source (`semantic-extractor`, `remember-tool`, `episodic-consolidator`, `transcript`). A retrieval step runs **before each reply**, embedding the user's query and injecting the most relevant facts into the prompt. No tool call required. No model cooperation needed.

Background jobs keep the store clean: nightly deduplication merges near-duplicates, and a curation job periodically distills scattered facts into a single, coherent `UserProfile`. Transcripts are embedded too, so the agent can semantically search what was actually said.
The store itself is pluggable, chosen at build time. Leave the Postgres extension out and the agent is genuinely **database-free** - markdown files plus an in-process embedding index (the default whenever no database backend is registered). Add https://github.com/pgvector/pgvector[pgvector] to keep the embeddings in a durable vector store instead; the bundled `app` ships the `hybrid` backend, keeping the markdown files as the source of truth and mirroring them into Postgres. The retrieval model is identical in every case.

The result: an agent that always knows who it is, who its owner is, and what it has learned - regardless of channel, context length, or the model's willingness to call tools.

Expand All @@ -29,7 +26,7 @@ Cognition::
A persistent `Soul` (identity, mood, focus) that shapes the agent's behavior every turn.

Memory::
pgvector-backed long-term memory with automatic retrieval, background curation, and transcript search. Always in context, never missed.
Long-term memory with automatic retrieval, background curation, and transcript search - always in context, never missed. File-based and database-free by default, with an optional pgvector backend for durable embeddings.

Skills::
Procedural memory the agent writes, uses, and curates itself: reusable `SKILL.md` how-to procedures, injected as an index each turn and loaded on demand.
Expand All @@ -40,6 +37,9 @@ Discord and Telegram adapters with voice in and out. Slack and WhatsApp adapters
Tools::
Google Workspace (Gmail, Calendar, Drive, Sheets, Storage), a brag-document tool, and a sandboxed shell.

Console::
An optional server-rendered admin UI at `/console`: a first-run setup wizard plus memory, skills, cognition, configuration, and schedule management. No Node build, no separate frontend.

Resilience::
A primary LLM with an Ollama fallback behind a circuit breaker.

Expand All @@ -51,6 +51,7 @@ xref:quickstart.adoc[Quick Start] - Get a working agent running in minutes.

* xref:architecture.adoc[Architecture] - How the modules fit together
* xref:composition.adoc[Composition] - Let agent.yml decide which capabilities are built in
* xref:console.adoc[Console] - The optional server-rendered admin UI
* xref:skills.adoc[Skills] - Procedural memory the agent writes and curates itself
* xref:google-workspace.adoc[Google Workspace] - Gmail, Calendar, Drive, Sheets, Storage
* xref:messaging.adoc[Messaging] - Discord and Telegram bots with voice
Expand Down
Loading
Loading