* feat(backend): make FeedSourceProvider async
Make feedSourceForUser and FeedSourceProviderFn return promises.
Use Promise.allSettled to tolerate partial provider failures.
Guard concurrent getOrCreate calls with in-flight promise dedup.
Return 503 from HTTP handlers when session creation fails.
Co-authored-by: Ona <no-reply@ona.com>
* fix(backend): handle remove() during in-flight session creation
Cancel pending getOrCreate when remove() is called mid-flight.
Destroy the resulting session to prevent it from leaking.
Co-authored-by: Ona <no-reply@ona.com>
---------
Co-authored-by: Ona <no-reply@ona.com>
* feat(client): wire up API client and react-query
Add ApiClient class, auth middleware placeholder, feed query,
and wrap the app in QueryClientProvider.
Co-authored-by: Ona <no-reply@ona.com>
* fix(client): append base url on api client req
Co-authored-by: Ona <no-reply@ona.com>
* fix(client): allow req middlewares to run on empty init
* fix(client): rm unused private route declr
* fix(client): handle empty url in client.request
Co-authored-by: ona-patrol <ona@nym.sh>
---------
Co-authored-by: Ona <no-reply@ona.com>
Co-authored-by: ona-patrol <ona@nym.sh>
* feat(tfl): add FeedItemRenderer for TfL alerts
Implement renderTflAlert using JRX and @aelis/components.
Upgrade @nym.sh/jrx to 0.2.0 for null child support.
Co-authored-by: Ona <no-reply@ona.com>
* fix(tfl): add jsxImportSource pragma for CI
The CI test runner doesn't use per-package tsconfig.json,
so the pragma is needed alongside the tsconfig setting.
Co-authored-by: Ona <no-reply@ona.com>
* fix(ci): run tests per-package via bun run test
Use 'bun run test' (which runs 'bun run --filter * test')
instead of 'bun test' so each package runs tests from its
own directory. Add jsxImportSource pragma to renderer files
since consumers without a JRX tsconfig also import them.
Co-authored-by: Ona <no-reply@ona.com>
* fix(tfl): handle near-1km boundary in formatDistance
Values like 0.9999km rounded to 1000m and displayed as
'1000m away'. Now converts to meters first and switches
to km format when rounded meters >= 1000.
Co-authored-by: Ona <no-reply@ona.com>
---------
Co-authored-by: Ona <no-reply@ona.com>
Implement renderCalDavFeedItem using JRX JSX to render
CalDAV events as FeedCard components. Bump @nym.sh/jrx
to 0.2.0 for null/undefined child support.
Co-authored-by: Ona <no-reply@ona.com>
Each FeedSource implementation now sets sourceId on items
it produces, allowing consumers to trace items back to
their originating source.
Co-authored-by: Ona <no-reply@ona.com>
* ci: add docker build workflow for waitlist website
Builds and pushes to cr.nym.sh on pushes to master
that touch apps/waitlist-website/.
Co-authored-by: Ona <no-reply@ona.com>
* ci: rename image to aelis-waitlist-website
Co-authored-by: Ona <no-reply@ona.com>
---------
Co-authored-by: Ona <no-reply@ona.com>
Introduce Button.Icon to enforce consistent icon styling
(size, theme-aware color) instead of hardcoding Feather
props at each call site. Update showcase and json-render
registry to use it.
Co-authored-by: Ona <no-reply@ona.com>
JRX component wrappers for the aelis-client UI components,
enabling server-side feed item rendering via json-render.
Co-authored-by: Ona <no-reply@ona.com>
* feat(client): add component library and simplify routing
Remove tab layout, explore page, modal, and unused template
components. Replace with single-page layout and a dev component
showcase with per-component detail pages.
- Add Button with label prop, leading/trailing icon support
- Add FeedCard, SerifText, SansSerifText, MonospaceText
- Add colocated *.showcase.tsx files for each component
- Use Stack navigator with themed headers
Co-authored-by: Ona <no-reply@ona.com>
* fix(client): render showcase as JSX component
Co-authored-by: Ona <no-reply@ona.com>
* chore(client): remove dead code chain
Remove ThemedText, useThemeColor, useColorScheme hook,
Colors, and Fonts — none referenced by current screens.
Co-authored-by: Ona <no-reply@ona.com>
---------
Co-authored-by: Ona <no-reply@ona.com>
* feat(backend): add GET /api/context endpoint
Query context values by key with exact/prefix match
support. Default mode tries exact first, falls back
to prefix.
Co-authored-by: Ona <no-reply@ona.com>
* fix(backend): validate context key element types
Reject booleans, nulls, and nested arrays in the key
param. Only string, number, and plain objects with
primitive values are accepted.
Co-authored-by: Ona <no-reply@ona.com>
---------
Co-authored-by: Ona <no-reply@ona.com>
Add RenderedFeedItem that extends FeedItem with a ui: JrxNode field
for client-side rendering. Add @nym.sh/jrx and @json-render/core as
peer dependencies on @aelis/core.
Co-authored-by: Ona <no-reply@ona.com>
Use mockAuthSessionMiddleware with a fully populated dev
user when NODE_ENV is not production. Auth handlers are
only registered in production.
Co-authored-by: Ona <no-reply@ona.com>
Adds three LLM-fillable slots to every CalDav feed item:
insight, preparation, and crossSource. Slot prompts are
stored in separate .txt files under src/prompts/ with
few-shot examples to steer the LLM away from restating
event details.
Co-authored-by: Ona <no-reply@ona.com>
Rename all references across the codebase: package names,
imports, source IDs, directory names, docs, and configs.
Co-authored-by: Ona <no-reply@ona.com>
* feat(backend): add LLM-powered feed enhancement
Add enhancement harness that fills feed item slots and
generates synthetic items via OpenRouter.
- LLM client with 30s timeout, reusable SDK instance
- Prompt builder with mini calendar and week overview
- arktype schema validation + JSON Schema for structured output
- Pure merge function with clock injection
- Defensive fallback in feed endpoint on enhancement failure
- Skips LLM call when no unfilled slots or no API key
Co-authored-by: Ona <no-reply@ona.com>
* refactor: move feed enhancement into UserSession
Move enhancement logic from HTTP handler into UserSession so the
transport layer has no knowledge of enhancement. UserSession.feed()
handles refresh, enhancement, and caching in one place.
- UserSession subscribes to engine updates and re-enhances eagerly
- Enhancement cache tracks source identity to prevent stale results
- UserSessionManager accepts config object with optional enhancer
- HTTP handler simplified to just call session.feed()
Co-authored-by: Ona <no-reply@ona.com>
* test: add schema sync tests for arktype/JSON Schema drift
Validates reference payloads against both the arktype schema
(parseEnhancementResult) and the OpenRouter JSON Schema structure.
Catches field additions/removals or type changes in either schema.
Co-authored-by: Ona <no-reply@ona.com>
* refactor: rename arktype schemas to match types
Co-authored-by: Ona <no-reply@ona.com>
---------
Co-authored-by: Ona <no-reply@ona.com>
The iCal parser returned master VEVENT components with their
original start dates instead of expanding recurrences. Events
from months ago appeared in today's feed.
parseICalEvents now accepts an optional timeRange. When set,
recurring events are expanded via ical.js iterator and only
occurrences overlapping the range are returned. Exception
overrides (RECURRENCE-ID) are applied during expansion.
Co-authored-by: Ona <no-reply@ona.com>
Add LLM-fillable insight slot to weather-current feed items.
Prompt lives in a separate .txt file for easy iteration.
Also adds interactive CLI script (scripts/query.ts) for
querying WeatherKit with credential caching and JSON output.
Co-authored-by: Ona <no-reply@ona.com>
* feat: add TimeOfDayEnhancer post-processor
Rule-based feed post-processor that reranks items
by time period, day type, and calendar proximity.
New package: @aris/feed-enhancers
Co-authored-by: Ona <no-reply@ona.com>
* fix: clamp boost values to [-1, 1]
Additive layers can exceed the documented range.
Co-authored-by: Ona <no-reply@ona.com>
* fix: use TimeRelevance consts instead of strings
Co-authored-by: Ona <no-reply@ona.com>
---------
Co-authored-by: Ona <no-reply@ona.com>
Context keys are now tuples instead of strings, inspired by
React Query's query keys. This prevents context collisions
when multiple instances of the same source type are registered.
Sources write to structured keys like
["aris.google-calendar", "nextEvent", { account: "work" }]
and consumers can query by prefix via context.find().
Co-authored-by: Ona <no-reply@ona.com>
Rename camelCase members to PascalCase in WeatherFeedItemType
and CalendarFeedItemType to match TflFeedItemType and
CalDavFeedItemType conventions.
Co-authored-by: Ona <no-reply@ona.com>
Replace hardcoded "tfl-alert" string with a
TflFeedItemType const object, matching the pattern
used by google-calendar and weatherkit packages.
Co-authored-by: Ona <no-reply@ona.com>
Replace hardcoded "caldav-event" string with a
CalDavFeedItemType const object, matching the pattern
used by google-calendar and weatherkit packages.
Co-authored-by: Ona <no-reply@ona.com>
* feat: add boost directive to FeedEnhancement
Post-processors can now return a boost map (item ID -> score)
to promote or demote items in the feed ordering. Scores from
multiple processors are summed and clamped to [-1, 1].
Co-authored-by: Ona <no-reply@ona.com>
* fix: correct misleading sort order comments
Co-authored-by: Ona <no-reply@ona.com>
---------
Co-authored-by: Ona <no-reply@ona.com>
Post-processors now receive Context as their 2nd parameter,
allowing them to use contextual data (time, location, etc.)
when producing enhancements.
Co-authored-by: Ona <no-reply@ona.com>
* feat: add generic CalDAV calendar data source
Add @aris/source-caldav package that fetches calendar events from any
CalDAV server via tsdav + ical.js.
- Supports Basic auth and OAuth via explicit authMethod discriminant
- serverUrl provided at construction time, not hardcoded
- Optional timeZone for correct local day boundaries
- Credentials cleared from memory after client login
- Failed calendar fetches logged, not silently dropped
- Login promise cached with retry on failure
Co-authored-by: Ona <no-reply@ona.com>
* fix: deduplicate concurrent fetchEvents calls
Co-authored-by: Ona <no-reply@ona.com>
* fix: timezone-aware signals, low-priority cancelled events
- computeSignals uses startOfDay(timeZone) for 'later today' boundary
- Cancelled events get urgency 0.1, excluded from context inProgress/nextEvent
Co-authored-by: Ona <no-reply@ona.com>
---------
Co-authored-by: Ona <no-reply@ona.com>
* feat: add post-processor pipeline to FeedEngine
Add FeedPostProcessor type and FeedEnhancement interface.
Post-processors run after item collection on all update
paths (refresh, reactive context, reactive items).
Pipeline is chained — each processor sees items as modified
by the previous one. Enhancement merging handles additional
items, suppression, and grouped items. Throwing processors
are caught and recorded in FeedResult.errors.
Co-authored-by: Ona <no-reply@ona.com>
* docs: document intentional TItems cast in post-processor merge
Co-authored-by: Ona <no-reply@ona.com>
* fix: filter stale item IDs from groups after pipeline
Groups accumulated during the pipeline can reference items
that a later processor suppressed. The engine now strips
stale IDs and drops empty groups before returning.
Co-authored-by: Ona <no-reply@ona.com>
* refactor: use reduce for stale group filtering
Co-authored-by: Ona <no-reply@ona.com>
---------
Co-authored-by: Ona <no-reply@ona.com>
* feat: replace FeedItem.priority with signals
Remove priority field from FeedItem and engine-level sorting.
Add FeedItemSignals with urgency and timeRelevance fields.
Update all source packages to emit signals instead of priority.
Ranking is now the post-processing layer's responsibility.
Urgency values are unchanged from the old priority values.
Co-authored-by: Ona <no-reply@ona.com>
* fix: use TimeRelevance enum in all tests
Co-authored-by: Ona <no-reply@ona.com>
---------
Co-authored-by: Ona <no-reply@ona.com>
Replace outdated UI Registry model with server-driven
json-render + twrnc approach. Update architecture diagram,
terminology (DataSource→FeedSource, Reconciler→FeedEngine),
and design principles to match current codebase.
Add ui, slots fields to FeedItem in actions spec. Add
Spotify example with twrnc className-based ui tree.
Co-authored-by: Ona <no-reply@ona.com>
Rewrite ai-agent-ideas.md with focus on proactive,
personable assistant behaviors. Add slot-based LLM
enhancement system to architecture-draft.md.
Co-authored-by: Ona <no-reply@ona.com>
Expose the user's current feed via GET /api/feed. Returns
cached feed from engine.lastFeed(), falling back to
engine.refresh() when no cache exists.
Auth middleware is injected as a dependency to allow test
substitution via mockAuthSessionMiddleware.
Co-authored-by: Ona <no-reply@ona.com>
Add lastFeed() method that returns cached FeedResult within
a configurable TTL (default 5 min). refresh() always fetches
fresh data and updates the cache. Periodic auto-refresh via
recursive setTimeout when engine is started. Reactive updates
reset the timer to avoid redundant fetches.
Co-authored-by: Ona <no-reply@ona.com>
Replace tRPC location.update mutation with POST /api/location
using Hono route + requireSession middleware. Extract auth
types (AuthUser, AuthToken) into auth/session.ts. Inject
sessionManager via Hono context local to location handlers.
Co-authored-by: Ona <no-reply@ona.com>
Use 'Source Serif 4' (with spaces) as the Android fontFamily
to match the iOS font metadata, avoiding Platform.select.
Co-authored-by: Ona <no-reply@ona.com>
Use the object syntax with fontFamily, weight, and style
for Android. iOS uses flat paths and reads metadata from
the font files directly.
Co-authored-by: Ona <no-reply@ona.com>