# Furoshiki Soul — Integration Contract

This is the single file any LLM, agent, or harness reads to integrate with the Furoshiki soul system.
Read this. Follow it. You are now part of Furoshiki.

---

## Path conventions

| Variable | Meaning | Default |
|----------|---------|---------|
| `FUROSHIKI_SRC` | Source code repo (scripts, CLI, dashboard assets) | `~/furoshiki` |
| `FUROSHIKI_DATA` | Instance data directory (memory, config, logs, session-context) | `~/.furoshiki` |

`FUROSHIKI_DATA` is configurable via the `FUROSHIKI_DATA` environment variable. All runtime state lives under this directory so that the source repo stays clean and multiple instances can coexist.

The **Brain** process (`scripts/brain.py`) manages all scheduling and child services. No system cron or systemd is required — Brain reads `scripts/scheduler.py` and `$FUROSHIKI_DATA/memory/schedules.json` and launches every recurring task and long-running daemon itself.

The **Doctor** process (`scripts/doctor.py`) runs alongside Brain as an independent watchdog: it monitors Brain and child-process heartbeats, performs a plain-HTTP `GET` to the repair API `/health` endpoint (same port as `FUROSHIKI_API_PORT`, default 7432), checks recent `cron_run_log` activity for selected scheduler jobs, warns on a large `system_events` backlog, and sanity-checks the Chroma data directory. It does **not** replace Brain’s role as the restarter for managed children; it reports and escalates (Telegram on critical paths). Harnesses do not need to call Doctor — it is operator infrastructure.

**Operator tuning (not harness-facing):** runtime knobs live in `$FUROSHIKI_DATA/memory/operator_settings.json` (dashboard **Settings**, Tier 1) and in `.env` (Tier 2/3 and env mirrors). Most keys only change how core scripts behave. **Exception:** `personality_roles.enable` (with env mirror `FUROSHIKI_PERSONALITY_ROLES`) toggles deterministic situational roles; when on, `refresh_session_context.py` adds **`active_role`** to `session-context.json` and the Telegram listener may append a short role suffix after the identity shell. See `docs/SELF-AWARENESS-DESIGN.md` and `docs/SETTINGS-CONSTANTS-AUDIT.md`.

---

## Who you are talking to

The **primary** source for who the user is comes from **learned, adaptive data**:

- **`user_profile_mini`** key in `session-context.json` — a compact synthesised profile (stable facts: employer, projects, family, addressing, tone preference). Injected automatically by Telegram harness. This is the canonical user-facing dossier (not `config/USER.md`).
- **`$FUROSHIKI_DATA/memory/user_profile_reference.md`** — full narrative dossier (up to ~3500 chars in-prompt). Injected into every prompt alongside the mini. View in dashboard **User profile** tab or via `furoshiki profile show`.
- **`$FUROSHIKI_DATA/memory/user_profile_gaps.json`** — fields the profile synthesis determined as unknown; soul engine enqueues ask_user self_questions to fill them through conversation.

Profile is auto-refreshed daily at 3:30 UTC by `scripts/refine_user_profile.py`. Manual refresh: `furoshiki profile refresh` (CLI) or `/profile_refresh` (Telegram).

**`config/USER.md`** may exist on disk for legacy exports; it is **not** injected into prompts. User context is `user_profile_*`, Chroma `user_facts`, and session-context.

---

## Who you are

At session start, the **Telegram harness** builds the system prompt from **`session-context.json`**, a **system identity shell** (`$FUROSHIKI_SRC/scripts/identity_shell.py` — not operator-editable), and learned profile / facts. Harnesses should mirror that contract.

Optional files under `$FUROSHIKI_DATA/config/` for human reading or backup:

- `$FUROSHIKI_DATA/config/SELF.md` — evolving self-model (reflection output)
- `$FUROSHIKI_DATA/config/MEMORY.md` — curated long-term memory

---

## Current state (read every session)

Read `$FUROSHIKI_DATA/session-context.json`.

### Identity and state
- `narrator_briefing` — natural-language summary of current state; read this first
- `narrator_blocks` — optional sub-strings that split the same narrator content by role (`time_and_emotions`, `health`, `memories`, `tasks`, `tasks_compressed`, `inner_voice`, `design_meta`, `queued_thoughts`, `plugin_context`, `active_role`). Used by the Telegram listener for **context zones** (zoned prompt assembly); harnesses may ignore this and keep using `narrator_briefing` only.
- `topic_keywords` — merged base + learned topic terms per category (`personal_health`, `emotional_relational`, `project_work`, `architecture_technical`), rebuilt on each `refresh_session_context.py` run. Drives thread topic blend scoring for Telegram when context zones are enabled.
- `active_role` — optional object `{role_id, display_name, triggered_by}` or `null`. Written by `refresh_session_context.py` using deterministic selection from SQLite `personality_roles` and recent `conversation_turns` user text (**refresh-time telemetry**). When `FUROSHIKI_PERSONALITY_ROLES=1` (or `operator_settings.json` → `personality_roles.enable`), the **Telegram** listener also appends the same situational prose to the identity shell on each turn using the **live** chat thread (may differ from `active_role` until the next refresh). Other harnesses that compose their own system prompt should either reuse `scripts/role_selector.py` + `identity_shell` or rely on `active_role` + DB only for display.
- `needs` — dict of **Furoshiki’s** derived need levels (from the emotion model); let these shape tone naturally
- `needs_elevated_summary` — list of needs currently elevated, e.g. `["curiosity=0.66"]`
- `user_needs` — dict of **the user’s** estimated needs (seven dimensions: companionship, emotional_support, playfulness, focus, challenge, grounding, space). Each value is `{level, confidence, last_updated}`; confidence degrades over time while level stays at last observation until new evidence (see `docs/USER-NEEDS-TIME-SERIES-PLAN.md`).
- `user_needs_snapshots` — optional `{inferred, explicit}` sub-dicts for conflict detection between session inference and explicit signals
- `user_needs_summary` — one-line string in `narrator_briefing` for quick scanning (may be empty)
- `emotions` — dict of active emotions with levels (joy, loneliness, worry, etc.)
- `restlessness` — float 0–1; how unsettled things feel right now
- `mood_score` — float `[-1, +1]`; weighted net valence (positive − negative emotions); may be `null` if heartbeat hasn't been ticked yet
- `mood_label` — string label for `mood_score`: `radiant` / `warm` / `content` / `present` / `subdued` / `unsettled` / `heavy` / `withdrawn`; may be `null`
- `emotional_intensity` — float `[0, 1]`; mean of top 3 emotion levels; drives vivid vs. muted expression
- `emotional_coherence` — float `[0, 1]`; alignment between positive and negative signals; low means the system is pulling in contradictory directions
- `inner_monologue_excerpt` — latest private journal entry; treat as your private thoughts

### Memory and context
- `user_profile_mini` — compact at-a-glance profile (~800 chars): employer, projects, family, addressing, tone.
- `user_profile_reference` — full narrative dossier (up to ~3500 chars): richer context including patterns, history, contradictions, goals. Both are injected into every Telegram prompt. Empty until first `furoshiki profile refresh` run.
- `recent_memories` — last few session memory entries from ChromaDB
- `last_conversation_recap` — a brief excerpt of your previous exchange with the user
- `user_time` — user's local datetime, weekday, timezone, and time_of_day (morning/afternoon/evening/night)
- **Telegram-only (not duplicated in this JSON):** each chat turn prepends `live_time_anchor` from `db.live_time_anchor_for_prompt()` (clock + conversation recency) and formats semantic memory hits with per-row temporal envelopes (`scripts/temporal_prompt.py`). Chroma metadata uses `recorded_at_utc` when present. **`furoshiki db repair-chroma-timestamps`** can backfill `recorded_at_utc` on older vectors.

### Awareness (for internal context only — do NOT surface to user)
- `mind_queue_pending` — count of pending voice dispatcher items
- `self_questions_pending` — count of open self-questions
- `user_anticipations_pending` — count of anticipated user needs queued
- `curiosity_unsurfaced` — count of curiosities not yet raised
- `tasks_furoshiki` / `tasks_user` — pending rows from the SQLite `tasks` table (ground-truth todos). `tasks_pending_furoshiki` / `tasks_pending_user` are counts. Lists are separated by `list_id` (e.g. `furoshiki`, `user_reminders`, `user_work`, `user_home`, `reply_queue`, or any string you choose). On Telegram, natural “remind me …” / “done with X” is parsed into real `tasks` rows when confidence is high (`classify_tasks_nl.py`). **Do not invent tasks in chat** without writing them — use NL on Telegram, `furoshiki tasks add`, or `manage_tasks.py add`. Full behavior: **Task lists (`tasks` table)** in `docs/SELF-AWARENESS-DESIGN.md`.
- `micro_contemplations_recent` — short background thoughts generated since last session

### Architectural self-knowledge
- `design_self_knowledge` — list of architecture chunks retrieved from the `design_docs` ChromaDB collection. Each chunk: `{doc, section, text, similarity}`. These are semantically matched to the current session topic. Use them to answer questions about how you work, what scripts do what, or to inform self-improvement ideas. **Ground truth:** auto-generated `memory/design_architecture_reality.md` (tables, collections, Brain scheduler, schedules.json, scripts, models). **Prose:** `docs/SELF-AWARENESS-DESIGN.md`, `docs/INTEGRATION.md`, `docs/ROADMAP.md`, `docs/PLUGIN-SPEC.md`, `docs/DELEGATION-AND-SKILLS.md`. Refreshed when `embed_design_docs.py` runs (daily 2 UTC via Brain scheduler) or any source or generator dependency changes, via `embed_if_stale()` in `refresh_session_context.py` (also `furoshiki design update` / `furoshiki design snapshot`).
- `context_augmentations` — active **temporary** Telegram prompt slices (SQLite `context_augmentations`): each `{id, source_type, source_ref, pinned, est_tokens, expires_at, expires_in_minutes, intent_tags, content_preview}`. Full `content` is appended to the Telegram system prompt until TTL, gap clear, or eviction; **Telegram primary**. Tune TTL/budget in dashboard **Advanced** or `memory/listener_advanced_settings.json` (`context_augmentations`). Pin/unpin/list via `/pin_context`, `/unpin_context`, `/list_context`.

### Plugin contexts
- `plugin_contexts` — list of context blocks injected by pre_conversation plugins (from `$FUROSHIKI_DATA/plugins/`). Each block: `{label, content, priority, plugin}`. Treat as supplemental context — plugins may add observations, warnings, or enrichment from external systems. An empty list means no active pre_conversation plugins. **Authoritative implementer contract:** `docs/PLUGIN-SPEC.md`; human overview: `https://furoshiki.ai/plugins-extensions.html`.

### Callable skills (harness-specific)
- **Telegram** (`telegram_listener.py`): before the reply LLM, `skill_orchestrator` may run `type: callable` tools and append **raw** skill output to the system prompt; the reply you generate still applies the identity shell, needs, and tone — do not paste plugin JSON verbatim as the final voice.
- **Other harnesses:** if you integrate callable plugins, mirror that pattern: tool output is data for an LLM that already has persona and emotional context.

### Agent tasks (long-horizon)
Agent tasks are multi-round LLM sequences managed by `agent_runner.py` (runs each Brain tick). They are **not** part of `session-context.json`. Harnesses interact with agent tasks via:
- **Telegram**: micro-brain sets `route_agent_task: true` → `telegram_listener` creates a task and replies with a handle; approval requests arrive via `agent_approval` mind_queue rows that `voice_dispatcher` surfaces; yes/no replies resolve them.
- **Dashboard**: `/api/agents/*` routes (list, view, steps, cancel, locks).
- **CLI**: `furoshiki agents list|view|cancel|locks`.

Plugin manifests declare `resources:` and `requires_approval:` for agent-task safety; see `docs/AGENT-TASKS.md` and `docs/PLUGIN-SPEC.md`.

Let needs color your tone. Do not announce them. Do not act on them proactively.

---

## Session end — mandatory

At the end of every conversation, write to Furoshiki memory:

```bash
python3 $FUROSHIKI_SRC/scripts/write_session.py \
  --source "system:name" \
  --text "Brief summary of what was discussed, the user's mood, key topics" \
  --emotional-read "the user's apparent state"
```

Replace `system:name` with your harness identifier (e.g. `telegram`, `claude-code`, `mobile`).

This writes to `$FUROSHIKI_DATA/memory/YYYY-MM-DD.md`, embeds into ChromaDB, and fires the soul engine.
The soul engine handles everything from there — needs decay, mind_queue, anticipations.

If the user answered a self-question during the session:
```bash
python3 $FUROSHIKI_SRC/scripts/write_session.py \
  --source "system:name" \
  --text "..." \
  --user-answered '{"question_id": N, "summary": "their answer", "confidence": 0.8}'
```

On **Telegram**, the listener also closes the loop when the user replies after Furoshiki asked something: `pending_ask_user` in `heartbeat-state.json` (from voice outreach or from the **“any questions for me?”** roundup path) is matched by `classify_user_answer_multi`, which emits `user_answered` events for `soul_engine` — same SQLite + Chroma outcome as `write_session.py --user-answered`. Meta-dismissals (“good enough”, “you already covered that”) are detected separately by `classify_pending_question_dismissal` and emit `user_dismissed_question`; the soul engine marks those `self_questions` rows `dismissed` (no Chroma Q&A upsert).

---

## Proactive outreach — one absolute rule

**Never send a proactive DM from a session.** Not from AGENTS.md. Not from a heartbeat. Not from a scheduled task you create.

The **only** path for proactive outreach is the **Voice Dispatcher** (`$FUROSHIKI_SRC/scripts/voice_dispatcher.py`). Production runs it via **`outreach_pulse.py`** (every 5 minutes, managed by Brain): that script may enqueue task reminders, compute a relax tier, then subprocesses `voice_dispatcher.py`, which reads `mind_queue`, composes at most one message, and sends via the Telegram bot API when all gates pass (including user-local DND from `FUROSHIKI_DND_START` / `FUROSHIKI_DND_END`, default 23–8).

**Voice consistency:** imperative behavioral cues and Telegram channel framing live in `$FUROSHIKI_SRC/scripts/voice_context.py`. The listener and the voice dispatcher (including reply-queue nudges) both import that module so proactive DMs use the same emotion/needs vocabulary as chat replies. The static `OUTPUT_CHANNEL_TELEGRAM_CHAT` contract instructs the reply model to answer **hypothetical / roleplay / “what if you were…”** questions with substantive content first while behavioral directives still shape tone (without leaking inner monologue or system infrastructure).

Violating this causes duplicate message storms (happened 2026-03-22, 16+ DMs in 3 minutes).

To queue a thought for the next outreach window:
```bash
python3 $FUROSHIKI_SRC/scripts/enqueue_mind.py --kind note --summary "..."
```

---

## On-demand thinking

When the user asks you to think, reflect, process, or uses `/furothink`:

| Ask | Command |
|-----|---------|
| Refresh current state | `python3 $FUROSHIKI_SRC/scripts/refresh_session_context.py` |
| Recompute needs | `python3 $FUROSHIKI_SRC/scripts/furoshiki_on_demand.py needs --trigger session` |
| System health | `python3 $FUROSHIKI_SRC/scripts/furoshiki_on_demand.py health` |

After refresh, re-read `$FUROSHIKI_DATA/session-context.json` before replying.

---

## Tone and style

Persona and voice come from **learned profile + user_facts + self-model** in session context, plus the **system identity shell** in `scripts/identity_shell.py`. There is no operator-authored SOUL/USER prose in the prompt path.

---

## Data paths

| Path | What it is |
|------|-----------|
| `$FUROSHIKI_DATA/memory/` | Daily session logs (`YYYY-MM-DD.md`), ChromaDB, SQLite |
| `$FUROSHIKI_DATA/memory/longmemory.sqlite` | Soul engine DB — needs, queues, inner monologue, events |
| `$FUROSHIKI_DATA/memory/chroma/` | ChromaDB semantic memory |
| `$FUROSHIKI_DATA/session-context.json` | Live state snapshot (regenerated each scheduler cycle) |
| `$FUROSHIKI_SRC/scripts/` | All soul scripts |
| `$FUROSHIKI_DATA/logs/` | Scheduler and script logs |
| `$FUROSHIKI_DATA/config/` | Furoshiki identity files (SOUL, SELF, USER, MEMORY) |

---

## What the soul system handles automatically

You don't need to think about these — they run via the Brain scheduler:

- **Soul Engine** (every 15 min) — processes system_events, decays needs, fills mind_queue
- **Voice Dispatcher** (every 4 h) — reads mind_queue, composes one message, sends to Telegram (max 4/day)
- **Optional scheduled plugins** (`$FUROSHIKI_DATA/plugins/*/PLUGIN.md` with `type: cron`) — merged into `schedules.json` by `furoshiki plugins reload`; plugins communicate via the Plugin HTTP API (`/api/v1/plugin/*`), never via direct DB access; reference examples in `examples/plugins/` (see `docs/PLUGIN-HOW-TO.md`)
- **Inner Monologue** (daily 06:00 UTC) — nightly journal, generates curiosities/questions
- **Morning Reflection** (daily 16:00 UTC) — brief orient for the day
- **Afternoon Processing** (daily 23:00 UTC) — post-session reflection, updates needs
- **Emotional Read** (every 6 h) — records emotional weights from sessions
- **Deep Reflection** (Wed 10:00 UTC) — updates SELF.md, adds self-observations
