Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
8286e88
Load Misumi seed order in runtime prompts
tyecam1 Jul 5, 2026
7589d1c
ci: skip dependency review without manifest changes
tyecam1 Jul 5, 2026
6f31bb6
Merge pull request #3 from tyecam1/codex/misumi-seed-order-runtime-lo…
tyecam1 Jul 5, 2026
9608476
fix: harden typed chat validation
tyecam1 Jul 6, 2026
ae0aa98
Merge pull request #4 from tyecam1/codex/harden-typed-chat-validation
tyecam1 Jul 6, 2026
02af7bd
fix: normalize chat stream payloads
tyecam1 Jul 6, 2026
fa1bf2b
Merge pull request #5 from tyecam1/codex/normalize-chat-stream-payload
tyecam1 Jul 6, 2026
9b07ab3
feat(ops): add misumi host readiness and lifecycle
tyecam1 Jul 10, 2026
bb24340
feat(personas): add enforced misumi persona policy
tyecam1 Jul 10, 2026
b5c839b
feat(misumi): add read-only household and task routing core
tyecam1 Jul 10, 2026
7020be8
feat(skills): add safe persona skill library and intake guards
tyecam1 Jul 10, 2026
5e27b6c
feat(misumi): add compatibility and status api
tyecam1 Jul 10, 2026
77c885f
docs(misumi): document compatibility api contract
tyecam1 Jul 10, 2026
080143c
feat(observability): add misumi evals and autonomy pilots
tyecam1 Jul 10, 2026
ace9fa2
fix(ops): persist misumi runtime settings
tyecam1 Jul 10, 2026
4af9df2
feat(auth): add scoped misumi interface token profile
tyecam1 Jul 10, 2026
7e3cfc7
fix(ops): tolerate uvicorn stderr on windows
tyecam1 Jul 10, 2026
8de7466
fix(ops): supervise uvicorn crash recovery
tyecam1 Jul 10, 2026
d42857f
fix(household): route queries to canonical domains
tyecam1 Jul 10, 2026
5120c2e
fix(household): keep open loops in active queues
tyecam1 Jul 10, 2026
cee15fb
feat(ops): add disabled autonomy task definitions
tyecam1 Jul 10, 2026
ffd506d
Merge pull request #6 from tyecam1/codex/misumi-control-plane
tyecam1 Jul 10, 2026
0619e01
fix: TTS available crashes on non-string tts_provider (#2034)
afonsopc Jul 11, 2026
13be152
fix(mcp): guard DbTokenStorage against non-dict oauth_tokens JSON (#5…
jagadish-zentiti Jul 11, 2026
09c43e6
fix: odysseus-memory cmd_add crashes on non-dict existing memory row …
afonsopc Jul 11, 2026
b19c480
fix(email): atomically claim scheduled emails before sending (#5110)
jagadish-zentiti Jul 11, 2026
831a7dc
feat(misumi): add append-only passive memory API
tyecam1 Jul 11, 2026
b2e2e4c
feat(misumi): add disabled local memory digest pilot
tyecam1 Jul 11, 2026
b834b53
test(misumi): cover passive memory and safety boundaries
tyecam1 Jul 11, 2026
b3af908
docs(misumi): document passive memory and digest operations
tyecam1 Jul 11, 2026
70303e4
Merge PR #7: complete Misumi Phase A passive memory
tyecam1 Jul 11, 2026
ac70c14
fix(misumi): accept interface string context
tyecam1 Jul 11, 2026
b7d44a3
Merge PR #8: accept interface string context
tyecam1 Jul 11, 2026
9566831
fix(misumi): route interface context as prompt
tyecam1 Jul 11, 2026
7312b20
Merge PR #9: route interface context as prompt
tyecam1 Jul 11, 2026
a55925a
fix(misumi): require relevant general retrieval hits
tyecam1 Jul 11, 2026
a467be5
Merge PR #10: require relevant general retrieval hits
tyecam1 Jul 11, 2026
940fc0c
fix(misumi): match household search tokens exactly (#11)
tyecam1 Jul 11, 2026
7118120
fix(misumi): ignore common retrieval stopwords
tyecam1 Jul 11, 2026
12c7752
Merge pull request #12 from tyecam1/codex/fix-misumi-common-stopwords
tyecam1 Jul 11, 2026
66f0b3b
fix(misumi): surface real model errors and handle reasoning-only replies
tyecam1 Jul 11, 2026
ff96350
feat(misumi): inject persona capabilities into the reply context
tyecam1 Jul 11, 2026
e7b0ac7
Merge pull request #13 from tyecam1/fix/misumi-model-reply
tyecam1 Jul 12, 2026
d05b4d3
Merge pull request #14 from tyecam1/feat/persona-capabilities
tyecam1 Jul 12, 2026
7bcfcde
feat(misumi): bounded within-request persona consultation
tyecam1 Jul 12, 2026
1e652a5
Merge pull request #15 from tyecam1/feat/misumi-consultation
tyecam1 Jul 12, 2026
b5a8ee9
fix(misumi): suppress native thinking and restore route logs
tyecam1 Jul 12, 2026
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
17 changes: 17 additions & 0 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,24 @@ jobs:
with:
persist-credentials: false

- name: Detect dependency manifest changes
id: dependency-files
shell: bash
run: |
set -euo pipefail
base_sha="${{ github.event.pull_request.base.sha }}"
head_sha="${{ github.event.pull_request.head.sha }}"
git fetch --no-tags --depth=1 origin "$base_sha" "$head_sha"
changed_files="$(git diff --name-only "$base_sha" "$head_sha")"
if printf '%s\n' "$changed_files" | grep -Eq '(^|/)(requirements[^/]*\.txt|pyproject\.toml|poetry\.lock|Pipfile(\.lock)?|package(-lock)?\.json|pnpm-lock\.yaml|yarn\.lock|uv\.lock|Cargo\.(toml|lock)|go\.(mod|sum))$'; then
echo "changed=true" >> "$GITHUB_OUTPUT"
else
echo "changed=false" >> "$GITHUB_OUTPUT"
echo "No dependency manifest changes detected; skipping dependency-review."
fi

- name: Review dependency changes
if: steps.dependency-files.outputs.changed == 'true'
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
with:
# Fail the PR on any newly introduced moderate-or-worse advisory.
Expand Down
22 changes: 21 additions & 1 deletion app.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,15 @@ def register_static_mime_types() -> None:

from src.app_helpers import abs_join
from src.generated_images import GENERATED_IMAGE_HEADERS, resolve_generated_image_path
from src.logging_config import configure_route_logging
from starlette.responses import RedirectResponse

# ========= LOGGING =========
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
)
configure_route_logging()
logger = logging.getLogger(__name__)

# ========= APP =========
Expand Down Expand Up @@ -180,6 +182,7 @@ async def dispatch(self, request, call_next):
"/api/auth/settings",
"/api/auth/integrations/presets",
"/api/health",
"/misumi/health",
"/api/version",
"/login",
}
Expand Down Expand Up @@ -663,6 +666,15 @@ async def web_search_error_handler(request: Request, exc: WebSearchError):
from routes.assistant_routes import setup_assistant_routes
app.include_router(setup_assistant_routes(task_scheduler))

# Misumi compatibility/control-plane surface. All routes except liveness are
# protected by the normal Odysseus auth middleware.
from routes.misumi_routes import setup_misumi_routes
app.include_router(setup_misumi_routes(
skills_manager,
task_scheduler=task_scheduler,
memory_vector=memory_vector,
))

# Calendar (CalDAV)
from routes.calendar_routes import setup_calendar_routes
calendar_router = setup_calendar_routes()
Expand Down Expand Up @@ -843,7 +855,11 @@ async def readiness_check() -> JSONResponse:
subsystem is whole, so an orchestrator can gate traffic on real readiness.
"""
from src.readiness import check_readiness
result = check_readiness()
result = check_readiness(
skills_manager=skills_manager,
task_scheduler=task_scheduler,
memory_vector=memory_vector,
)
return JSONResponse(status_code=200 if result.get("ready") else 503, content=result)

@app.get("/api/runtime")
Expand Down Expand Up @@ -1049,6 +1065,10 @@ async def _ensure_default_tasks():
changed = skills_manager.backfill_owner(primary_owner, set(users.keys()))
if changed:
logger.info("Assigned %s legacy skill file(s) to %s", changed, primary_owner)
from src.misumi_skills import seed_first_party_skills
seeded = seed_first_party_skills(skills_manager, primary_owner)
if seeded:
logger.info("Installed %s first-party Misumi skill(s) for %s", seeded, primary_owner)
except Exception as e:
logger.debug(f"Skill owner backfill skipped: {e}")

Expand Down
36 changes: 36 additions & 0 deletions config/misumi_autonomy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"phase": "A",
"enabled": false,
"pilots": {
"morning-status": {
"enabled": false,
"schedule": "manual-first; proposed daily 08:00",
"writes_allowed": false,
"external_sends_allowed": false
},
"skill-audit": {
"enabled": false,
"schedule": "manual-first; proposed weekly Sunday 02:00",
"writes_allowed": false,
"external_sends_allowed": false
},
"task-triage": {
"enabled": false,
"schedule": "manual-first; proposed daily 08:05",
"writes_allowed": false,
"external_sends_allowed": false
},
"household-qa": {
"enabled": false,
"schedule": "manual only",
"writes_allowed": false,
"external_sends_allowed": false
},
"memory-digest": {
"enabled": false,
"schedule": "manual only",
"writes_allowed": false,
"external_sends_allowed": false
}
}
}
68 changes: 68 additions & 0 deletions config/misumi_persona_policy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"aoteru": {
"role": "head-human-interfacer",
"allowed_skill_categories": ["routing", "decision", "system", "task-triage"],
"blocked_tools": ["email_send", "calendar_write"],
"default_mode": "plan"
},
"lelouch": {
"role": "operator",
"allowed_skill_categories": ["workflow", "runbook", "deployment", "task-execution"],
"blocked_tools": ["email_send"],
"default_mode": "execute_with_approval"
},
"kurisu": {
"role": "archivist",
"allowed_skill_categories": ["evidence", "archive", "citation", "document-analysis"],
"blocked_tools": ["shell_write", "email_send"],
"default_mode": "read_only"
},
"misato": {
"role": "caretaker",
"allowed_skill_categories": ["household-ops", "food", "rest", "routines"],
"blocked_tools": ["shell", "email_send"],
"default_mode": "assist"
},
"jin": {
"role": "selector",
"allowed_skill_categories": ["music", "records", "gigs", "collection"],
"blocked_tools": ["shell", "email_send"],
"default_mode": "read_only"
},
"sanji": {
"role": "chef",
"allowed_skill_categories": ["cooking", "shopping", "inventory", "meal-planning"],
"blocked_tools": ["shell", "email_send"],
"default_mode": "assist"
},
"l": {
"role": "detective-financer",
"allowed_skill_categories": ["budget", "anomaly", "receipt", "subscription"],
"blocked_tools": ["shell", "email_send", "bank_write"],
"default_mode": "read_only"
},
"ginko": {
"role": "naturalist",
"allowed_skill_categories": ["plants", "home-sensors", "weather", "damp"],
"blocked_tools": ["shell", "email_send"],
"default_mode": "read_only"
},
"ichigo": {
"role": "guardian",
"allowed_skill_categories": ["urgent-loops", "maintenance", "safety", "repairs"],
"blocked_tools": ["shell_write", "email_send"],
"default_mode": "assist"
},
"giorno": {
"role": "creative-spawner",
"allowed_skill_categories": ["routine-design", "experiments", "rollback", "improvement"],
"blocked_tools": ["shell", "email_send"],
"default_mode": "propose"
},
"erwin": {
"role": "deputy-general",
"allowed_skill_categories": ["priority", "risk", "strategy", "cost-of-delay"],
"blocked_tools": ["shell", "email_send"],
"default_mode": "plan"
}
}
149 changes: 149 additions & 0 deletions docs/misumi-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Misumi compatibility API

Odysseus exposes a Misumi-facing compatibility surface while retaining its generic UI and APIs. Personas select context; Odysseus enforces permissions.

Only `GET /misumi/health` is unauthenticated. Every other route uses normal Odysseus cookie or bearer-token auth. Create interface tokens with the `misumi_interface` profile, which grants only `misumi:read` and `misumi:execute`; the latter currently permits planning only because Phase A exposes no household write or task-execution path.

## Health

```http
GET /misumi/health
```

Reports liveness, Phase A, and household-root reachability. Use authenticated `GET /api/ready` and `GET /misumi/status` for dependency state.

## Respond

```http
POST /misumi/respond
Authorization: Bearer ody_...
Content-Type: application/json

{
"prompt": "What is on the shopping list?",
"persona": "sanji",
"mood": "focused"
}
```

Response:

```json
{
"text": "From household/food/shopping-list.md line 3: - [ ] miso",
"state": "speaking",
"mood": "focused",
"source": "odysseus",
"persona": "sanji",
"who": "Sanji",
"audio_url": null,
"voice": null,
"tts_provider": null,
"consulted": [],
"capsule_id": null,
"handoff_ids": [],
"sources": [
{"path": "household/food/shopping-list.md", "line": 3, "snippet": "- [ ] miso", "score": 1}
]
}
```

Repository-backed answers cite the canonical path and line. Missing data or model state degrades explicitly; no action is claimed.

For successful Aoteru model replies, bounded consultation may append labelled contributions from at most two personas. `consulted` identifies personas that returned a contribution, `capsule_id` references the single local consultation capsule, and `handoff_ids` references local handoff records created from successful contributions. These fields are presentation and reference data only: they grant no permission, approval, autonomy, task execution, external action, or household write access.

Consultations are synchronous within the initiating `/respond` request. They do not start background work or persona swarms. Set `MISUMI_CONSULT=false` to disable consultation; the default is enabled. When disabled, `/respond` follows the legacy path and omits the three additive fields.

## Task planning

```http
POST /misumi/task
Authorization: Bearer ody_...
Content-Type: application/json

{
"prompt": "autonomously complete agentic routed tasks",
"persona": "aoteru",
"mode": "task",
"approval": "none"
}
```

Task mode scans the documented file queues, ranks the critical path, and returns `planned` or `blocked` with candidates, files read, blockers, policy, and a handoff prompt. It never squeezes task state into chat text and never mutates the household repository.

Valid approval values are `none`, `plan_only`, `approved_read_only`, and `approved_execute`. Approval cannot enable Phase B household writes.

## Personas and skills

```http
GET /misumi/personas
GET /misumi/personas/kurisu/skills
POST /misumi/personas/kurisu/skills/audit
```

Skill lists are filtered by the versioned persona policy. Audits are admin-only and do not publish external skills.

## External skill intake

```http
POST /misumi/skills/import-draft
GET /misumi/skills/security-review/{skill}
```

Imports accept supported GitHub/skills.sh URLs through the existing constrained fetcher. Every external skill is forced to draft, scripts are stored only as text, no bundle code runs, and static security flags are returned. Publication requires a separate human/admin review path.

## Status

```http
GET /misumi/status
```

Reports readiness, household reachability and Git dirtiness, task counts, persona skill counts, recent event-log count, Phase A, and `writes_allowed: false`.

The additive `memory` object reports capsule, inbox, open-loop, stale-loop and pending-handoff counts together with the newest capture, top open loop, recommended action and responsible persona. `memory.writes_allowed` is always `false`; local memory persistence does not grant household writes.

## Passive memory

All write routes below require the existing `misumi:execute` scope. Read routes require `misumi:read`.

```http
POST /misumi/memory/capture
GET /misumi/memory/inbox?limit=20
GET /misumi/memory/recent?limit=20
GET /misumi/memory/open-loops
POST /misumi/memory/{id}/confirm
POST /misumi/memory/{id}/route
POST /misumi/memory/{id}/close
GET /misumi/glance
```

Capture accepts `text` plus optional `source`, `type`, `persona`, `entities`, `next_action`, and `meta`. It returns the full capsule. Explicit type and persona values override deterministic inference; invalid values return 422. Confirm, route, and close append a new version of the capsule. Listings fold these versions and report `corrupt_lines` rather than failing on malformed JSONL records.

Route body:

```json
{"persona_primary": "ichigo", "persona_secondary": "kurisu"}
```

Close body is optional and may contain `{"resolution": "verified locally"}`.

## Local handoffs

```http
POST /misumi/handoff
GET /misumi/handoffs
GET /misumi/handoffs?status=pending
POST /misumi/handoffs/{id}/resolve
```

A handoff contains `from_persona`, `to_persona`, a short local `action`, and optional `capsule_id` and `note`. It is a local coordination record only. Actions containing outbound operations such as email, notification, payment, transfer, posting or calling are rejected.

## Interface-box configuration

After side-by-side validation, set the box-local `agentUrl` to:

```text
http://DESKTOP-IN7O23D:420/misumi
```

The interface-box server appends `/respond`. Store `ODYSSEUS_API_TOKEN` only in the box process environment; its proxy adds the Authorization header server-side. Keep port 4500 as the fallback until end-to-end evals pass.
Loading
Loading