haiven-agent-briefing — User Guide

The briefing agent is the core AI agent for the Haiven platform. It generates structured, LLM-powered outputs across 9 scopes: daily briefings, document drafting, email composition, content publishing, and opportunity detection. All scopes go through a single POST /briefing endpoint.


Scopes at a Glance

Scope When to use
daily Morning: prioritized view of open tasks and recent KB updates
end_of_day End of day: completions count and carry-over open items
review Revise a task artifact using voice or text instructions
context_assembly Gather KB context for a task before starting work
pre_meeting Upcoming meeting prep with KB context (also runs on 15-min schedule)
document Draft a structured document from a task via Seed-36B
email Compose an email draft with GLM tone classification + Seed-36B body
opportunity Daily KB scan for blog ideas, patterns, and follow-ups
content_pipeline Publish blog (WordPress) + social snippets (LinkedIn, X) + podcast

Daily Briefings

Morning briefing

Fetches open tasks (up to 20), recently completed tasks (up to 10), and recent KB entries (up to 10). Generates a prioritized summary via GLM-4.7-Flash.

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d '{"scope": "daily", "notify": false}' | jq .briefing

With push notification

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d '{"scope": "daily", "notify": true}'

With a focus instruction

voice_instructions is injected as a system message before the template prompt:

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d '{"scope": "daily", "voice_instructions": "Focus on high-priority and overdue items only. Be brief."}'

With automatic task extraction

After generating the briefing, makes a second LLM call to extract actionable items and creates them as tasks in work-hub. Deduplicates against existing open tasks by title.

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d '{"scope": "daily", "extract_tasks": true, "notify": false}' \
  | jq '{briefing: .briefing, new_tasks: .created_task_ids}'

End-of-day summary

Counts completions, lists carry-over open tasks, produces a short wrap-up. No KB search.

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d '{"scope": "end_of_day", "notify": true}'

Working with Tasks

Assemble KB context for a task

Before starting work on a task, run context assembly to populate its context field with everything the knowledge base knows about the topic. The task status is set to context_ready when done.

TASK_ID="<uuid-from-work-hub>"

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d "{\"scope\": \"context_assembly\", \"task_id\": \"${TASK_ID}\"}" \
  | jq '{total_sources: .data_sources.total_sources, summary: .briefing}'

Four parallel KB queries run: meeting notes, research, references, and AI conversations — all scoped to the task title. Results are deduplicated by point_id.

Retrieve the assembled context afterwards:

curl -sf http://localhost:8030/api/v1/tasks/${TASK_ID} | jq .context

Revise a task artifact with voice instructions

Reads the current artifact from the task, searches the KB using your instructions as the query, then rewrites the artifact accordingly. Saves the result back to the task.

TASK_ID="<uuid-from-work-hub>"

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d "{
    \"scope\": \"review\",
    \"task_id\": \"${TASK_ID}\",
    \"voice_instructions\": \"Make it shorter, cut the jargon, and add a clear next action\"
  }"

Both task_id and voice_instructions are required. The revised text appears in briefing and is saved to the task immediately.


Document Drafting

Generates a structured document draft using a 7-step pipeline. KB context is assembled first, then Seed-36B writes the body.

Basic document draft

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d '{
    "scope": "document",
    "task_id": "your-task-uuid-here"
  }'

With subject and client context

Use pipe-separated keys in voice_instructions:

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d '{
    "scope": "document",
    "task_id": "your-task-uuid-here",
    "voice_instructions": "subject:Q1 Product Roadmap|client:Acme Corp"
  }'
Key Effect
subject:... Overrides the KB search topic and document subject
client:... Filters KB context to client-relevant material; injected as system context

With a custom template

Pass the Langfuse template name in the template field. Templates are managed at ai-ops.haiven.site. Falls back to the default if not found.

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d '{
    "scope": "document",
    "task_id": "your-task-uuid-here",
    "template": "proposal-template"
  }'

Email Composition

Two-model pipeline: GLM-4.7-Flash classifies the tone from context; Seed-36B writes the body.

Compose an email draft

Pass recipient and subject as pipe-separated keys in voice_instructions:

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d '{
    "scope": "email",
    "voice_instructions": "to:client@example.com|subject:Project Update — Week 12"
  }'

With style and client context

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d '{
    "scope": "email",
    "task_id": "optional-related-task-uuid",
    "voice_instructions": "to:team@company.com|subject:Q2 Planning Kickoff|style:casual|client:Internal"
  }'
Key Required Effect
to:... Yes Recipient email address
subject:... Yes Email subject line (also used as KB search query)
style:... No Override GLM's tone classification (e.g. formal, casual, concise)
client:... No Filter KB context to client-relevant material

The composed draft is returned in briefing. If task_id is provided, it is saved as a task artifact in work-hub.


Opportunity Detection

Scans the knowledge base for five types of signal. Results are cached in Redis after the daily scheduled run, so on-demand calls return the cache instantly if available.

On-demand scan

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d '{"scope": "opportunity", "notify": false}'

With notification delivery

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d '{"scope": "opportunity", "notify": true}'

The response data_sources.opportunities contains the full ranked list. Each opportunity has a type, title, rationale, and urgency_score.

Detection types:

Type What it finds
blog_idea Insights and lessons worth publishing
productization_pattern Reusable workflows that could become products
client_follow_up Open items and promises from client conversations
cross_pollination Ideas applicable across different domains
competitive_intel Market trends and competitor signals

Configure detection behavior

Edit /mnt/apps/src/haiven-agent-briefing/config/opportunity.yml:

enabled: true
detection_types:
  - blog_idea
  - productization_pattern
  - client_follow_up
  - cross_pollination
  - competitive_intel
output_channel: ntfy
max_opportunities: 10
urgency_threshold: 0.3

Opportunities below urgency_threshold are excluded. Changes take effect on the next container restart.


Content Publishing

Takes a work-hub task and publishes it across multiple channels.

Publish a blog post to WordPress

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d '{
    "scope": "content_pipeline",
    "task_id": "your-task-uuid-here",
    "voice_instructions": "targets:blog"
  }'

Posts are published to WordPress as drafts — not immediately live. Review at https://www.elijah.ai/wp-admin. If WordPress credentials are absent or unreachable, the Markdown draft is saved to /mnt/storage/drafts/ instead.

Blog + social snippets together

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d '{
    "scope": "content_pipeline",
    "task_id": "your-task-uuid-here",
    "voice_instructions": "targets:blog,social_linkedin,social_x|tone:professional|audience:technical"
  }'

Available publish targets

Target Output location
blog WordPress draft + /mnt/storage/drafts/ fallback
social_linkedin /mnt/storage/social/{date}-{slug}-snippets.txt
social_x Same file as LinkedIn, max 280 chars
podcast /mnt/storage/podcast/episodes/ + RSS feed update

Voice instruction keys

Key Default Effect
targets:... blog Comma-separated list of publish targets
tone:... professional Writing tone (e.g. casual, technical)
audience:... general Target audience (e.g. developers, executives)
client:... Filter KB context to client-relevant material

Check content pipeline output

# List recent post folders
ls -lt /mnt/storage/content-output/

# Inspect a post folder
ls /mnt/storage/content-output/2026-03-01-my-post-title/
# blog.md  social-linkedin.txt  social-x.txt  featured-image.png  social-image.png  manifest.json

cat /mnt/storage/content-output/2026-03-01-my-post-title/manifest.json

# Read social snippets
cat /mnt/storage/social/2026-03-01-my-post-title-snippets.txt

Pre-Meeting Briefings

Pre-meeting briefings run automatically every 15 minutes via APScheduler. They query the KB for upcoming calendar events (filtered to doc_type=calendar-event) and generate prep notes per meeting.

Trigger manually

curl -sf -X POST http://localhost:8035/briefing \
  -H "Content-Type: application/json" \
  -d '{"scope": "pre_meeting"}'

Events already briefed within the past 2 hours are skipped (idempotency via event UID, backed by Redis).

To disable the scheduler without affecting manual calls, set BRIEFING_PRE_MEETING_ENABLED=false.


Automatic Schedule

The agent fires automatically on weekdays via systemd timers. Both send notifications via notification-hub.

Timer Time Scope
haiven-briefing-morning.timer Mon–Fri 07:30 daily
haiven-briefing-eod.timer Mon–Fri 17:30 end_of_day

Both include a 2-minute randomized delay.

# Check timer status
systemctl list-timers haiven-briefing*

# View recent logs
journalctl -u haiven-briefing-morning.service -n 30
journalctl -u haiven-briefing-eod.service -n 30

# Trigger manually (as if the timer fired)
sudo systemctl start haiven-briefing-morning.service
sudo systemctl start haiven-briefing-eod.service

Agent Protocol (for integrations)

If you are calling the briefing agent from another service, use /v1/agent instead of /briefing. It accepts intent strings rather than scope names.

curl -sf -X POST http://localhost:8035/v1/agent \
  -H "Content-Type: application/json" \
  -d '{
    "user_message": "Draft a document for my current task",
    "intent": "draft",
    "task_id": "your-task-uuid-here",
    "entities": {"instruction": "client:Acme Corp"}
  }'
Intent Scope
briefing.daily daily
briefing.weekly end_of_day
scheduling.query pre_meeting
research.topic context_assembly
review_feedback review
draft document
opportunity.scan opportunity
email.compose email
content.publish content_pipeline

Custom Templates

Prompt templates for daily and end_of_day are managed in Langfuse:

  1. Open ai-ops.haiven.site (Langfuse)
  2. Go to Prompts
  3. Create or edit briefing-daily (variables: {{open_tasks}}, {{completed_tasks}}, {{recent_kb}})
  4. Or edit briefing-eod (variable: {{task_counts}})
  5. Save — the next call picks up the new version immediately

If Langfuse is unavailable, the service falls back to hardcoded templates in app/templates.py and continues functioning.


Troubleshooting

Briefing returns empty or garbled text

GLM-4.7-Flash has thinking mode ON by default — the service extracts the answer from reasoning_content automatically. If responses are empty, check LiteLLM is reachable:

curl -sf http://localhost:4000/health | jq .
curl -sf http://localhost:4000/v1/models | jq '.data[].id'

review or context_assembly returns 404

The task_id was not found in work-hub. Verify the task exists:

curl -sf http://localhost:8030/api/v1/tasks/<task_id>

context_assembly finds 0 sources

The KB may not have content on this topic yet. Test directly:

curl -sf -X POST http://localhost:8022/v1/search \
  -H "Content-Type: application/json" \
  -d '{"query": "<task title>", "limit": 5}'

WordPress publish fails silently

Check data_sources.blog.wp_status in the response:
- draft / pending — published successfully
- wp_unreachable_draft_saved — WP failed, Markdown saved to /mnt/storage/drafts/
- draft_saved_local — WP credentials not configured

Fix: verify BRIEFING_WP_API_USER and BRIEFING_WP_API_TOKEN in the service .env.

Opportunity scan returns no results

Lower urgency_threshold in config/opportunity.yml to 0.1 as a test. Also check Redis is reachable:

docker exec -it redis redis-cli ping

Pre-meeting briefing finds no meetings

The scope requires calendar events to be ingested into haiven-knowledge with doc_type=calendar-event. Verify the calendar ingestion pipeline is running.

Scheduled timers not firing

systemctl list-timers haiven-briefing*
systemctl cat haiven-briefing-morning.service

# Reload if config changed
sudo systemctl daemon-reload
sudo systemctl enable --now haiven-briefing-morning.timer haiven-briefing-eod.timer

Notifications not delivered

# Check notification-hub health
curl -sf http://localhost:8040/health

# Test a manual notification
curl -sf -X POST http://localhost:8040/notify \
  -H "Content-Type: application/json" \
  -d '{"source_agent": "haiven-agent-briefing", "title": "Test", "body": "ping"}'

# Check logs
docker logs -f notification-hub

Container fails to start

docker logs agent-briefing --tail 50

Common causes: Redis unreachable at startup (APScheduler fails to initialize), or missing required env vars (BRIEFING_LITELLM_URL, BRIEFING_WORKHUB_URL).


Viewing Traces in Langfuse

All LLM calls are traced. Open http://ai-ops.haiven.site to browse traces filtered by agent_type. Span names include: generate_body, wp_publish, social_generate, podcast_tts, podcast_ffmpeg, context_assembly.


Service Domain Purpose
work-hub work.haiven.site Task source and artifact store
haiven-knowledge knowledge.haiven.site Semantic KB used for context queries
notification-hub hub.haiven.site Delivers briefings via ntfy/TTS
LiteLLM llm.haiven.site LLM gateway (GLM + Seed-36B)
Langfuse ai-ops.haiven.site Prompt management and LLM tracing
WordPress www.elijah.ai Blog publishing target
chatterbox-tts Internal Podcast audio generation