Skip to content

feat(importer): cold-start import from other AI tools with EverOS HTTP backend#170

Merged
0xKT merged 29 commits into
mainfrom
feat/cold_start_import
Jul 22, 2026
Merged

feat(importer): cold-start import from other AI tools with EverOS HTTP backend#170
0xKT merged 29 commits into
mainfrom
feat/cold_start_import

Conversation

@Kendrick-Song

@Kendrick-Song Kendrick-Song commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Change description

Cold-start import: scan conversation history and memory files from other AI tools (starting with Claude Code), feed them into EverOS long-term memory so Raven knows the user from day one.

Key deliverables:

  • Importer framework (raven/importer/): Scanner protocol, ClaudeCodeScanner (JSONL conversations + memory/*.md files), orchestrator with batched store (100 msgs / 30K chars), idempotent state tracker with resume, cancel-flag stop mechanism
  • CLI commands (raven import scan/run/status/stop): interactive platform/tier selection, Rich progress bar, background execution support, cancel via raven import stop
  • Onboard integration (step 6): platform/tier/mode selection, foreground or background execution, wired after EverOS memory config
  • EverOS HTTP-only backend: removed embedded mode entirely, EverOS runs as an independent server on port 18791 with auto-start/health-check lifecycle; unlocks multi-process concurrency (TUI + gateway + background import)
  • Data alignment fixes: sender_id, app_id, project_id now omitted from import metadata so EverOS defaults match daily recall queries
  • Architecture cleanup: domain logic (scan_all, filter_by_tier) moved from CLI to importer layer; concurrent scanner support via asyncio.gather; state path uses config/paths.get_data_dir()

Type of change

  • New feature
  • Bug fix
  • Document
  • Others

Related issues (if there is)

Closes #104
Related to #176 (native-Windows EverOS memory: this PR adds the interim
win32 guard; the upstream EverOS fcntl->portalocker fix is tracked there)

Checklists

Development

  • Lint rules pass locally
  • Application changes have been tested thoroughly
  • Automated tests covering modified code pass

Security

  • Security impact of change has been considered
  • Code follows security best practices and guidelines

Code review

  • Pull request has a descriptive title and context useful to a reviewer. Screenshots or screencasts are attached as necessary

@Kendrick-Song
Kendrick-Song requested a review from 0xKT July 21, 2026 12:13

@0xKT 0xKT left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the diff; CI is green and test coverage is good. A few findings below, anchored inline and ordered roughly by impact. Nothing blocking, but items 1-4 are worth a look before merge.

Comment thread raven/plugin/memory/everos/backend.py Outdated
await _acquire_embedded_everos(self._logger)
self._embedded_started = True
if isinstance(self._adapter, _HttpEverosAdapter):
from raven.cli._everos_server import ensure_everos_server

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Layering: this is the only plugin-layer module that imports raven.cli. It is not a circular-import workaround (raven/cli/__init__.py is empty and _everos_server.py never imports back into the plugin), so the deferred import does not buy anything structurally -- the dependency direction is just backwards. ensure_everos_server is a server-lifecycle helper shared by the backend, onboard, and import; consider moving it to a lower layer (e.g. raven/memory_engine/ or under this plugin package) so nothing under raven/plugin/ reaches up into raven/cli/.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ecd02a9. Moved _everos_server.py from raven/cli/ to raven/plugin/memory/everos/_server.py. All import sites (backend, onboard, tests) updated.

Comment thread raven/plugin/memory/everos/backend.py Outdated
# Default timeout — HTTP mode is per-turn, so we keep it tight.
_DEFAULT_HTTP_TIMEOUT_S: float = 10.0
# Default timeout — per-turn, so we keep it tight.
_DEFAULT_HTTP_TIMEOUT_S: float = 360.0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment still says "keep it tight" while the value is now 36x larger (10s -> 360s). This timeout flows into the single httpx.AsyncClient used by both cold-start import and the live per-turn store path (AgentLoop._dispatch_backend_store -> backend.store at raven/agent/loop/main.py:965, which is awaited). raven-plugin.toml sets no timeout_s, so 360s is the effective per-turn default -- a hung EverOS server can block turn completion for up to 360s per httpx phase. Suggest a separate longer timeout for the bulk-import path, keeping the per-turn client tight, and fixing the now-contradictory comment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ecd02a9. Client default timeout set to 60s (recall, health). Memorize add/flush uses per-request timeout=360.0 override via httpx. Stale comment removed.

Comment thread raven/cli/tui_commands.py Outdated
# and its index lock is held for the session. No-op for http/no-op backends.
if agent_loop is not None and agent_loop.backend is not None:
try:
await agent_loop.backend.start()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This awaits backend.start() before serve_task = create_task(server.serve_forever()) and before the handshake wait. On main this was deliberately deferred to a background task created only after the handshake ("must not block the handshake / first render"). Since start() now calls ensure_everos_server() which can spawn the server and poll for up to 30s, and the Node RPC client has no handshake timeout, a cold start (server not yet up) leaves the terminal unrendered until start() returns. Note the retained comment "No-op for http/no-op backends" no longer holds -- HTTP is the only adapter now and its start() does real work. Consider restoring the post-handshake background start, or documenting the cold-start block as intentional.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ecd02a9. backend.start() moved to after handshake, runs as a background create_task so first render is not blocked.

Comment thread raven/importer/types.py Outdated
class ImportSession:
"""A complete importable unit ready for store()."""

app_id: str

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

app_id / project_id are set by the scanner (scanners/claude_code.py:365-366 etc.) but never read: orchestrator._flush sends only metadata={"is_final": ...}, so the params threaded through store() -> memorize() -> /add + /flush body (backend.py:391-392) are always None on every runtime path (the live AgentLoop.store call passes no metadata). If imports are meant to land in EverOS's default/default partition (as the orchestrator test comment states), drop the unused fields and backend params; otherwise wire these into the metadata. Carrying both halves disconnected reads as accidental.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ecd02a9. Removed app_id/project_id from ImportSession dataclass, scanner, and all test sites. Dead helper _project_dir_from_path also removed.

try:
session = await scanner.read(result)
await _feed_session(backend, session)
state.mark_submitted(platform, key)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resume granularity is per source unit, not per batch: mark_submitted runs only after the whole session feeds, and state.is_submitted treats a "failed" entry as not-done. A multi-batch conversation (>100 msgs or >30k chars) that fails mid-way re-reads and re-sends already-accepted batches on retry. Whether that duplicates depends on EverOS-side dedup (not visible from this repo). Fine for memory files; worth a per-batch checkpoint or at least a note if large conversations are common.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged. Added a NOTE comment at the checkpoint site explaining the per-source-unit granularity, EverOS session_id dedup safety, and that per-batch checkpoint is deferred until full-conversation import is common. (ecd02a9)

Comment thread raven/cli/_everos_server.py Outdated
return

port = _extract_port(base_url)
await asyncio.to_thread(_start_server, port)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlocked probe-then-spawn: concurrent processes (tui + gateway + import) starting while the server is down each pass the health probe and each spawn everos server start on the same port; the losers die on port-in-use. The poll loop still converges so correctness holds, but the child exit status is never checked, so this leaves stray failed children plus misleading "started everos server" log lines. A pidfile/lock would make it clean.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ecd02a9. Added fcntl.flock non-blocking try-lock in _start_server_if_unlocked(). Only the process that acquires the lock spawns the server; others skip spawn and go straight to health poll. Lock file at ~/.raven/everos-server.lock.

Comment thread raven/importer/state.py Outdated
except (json.JSONDecodeError, ValueError):
backup = self._path.with_suffix(".json.corrupt")
logger.warning(
"Corrupt import state at %s -- backing up to %s",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loguru uses {}-style formatting, not printf. This %s call emits a literal "%s" and drops both paths (loguru silently ignores the extra positional args). Switch to {}.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ecd02a9. Changed %s to {} in the loguru warning call.

4. EverOS long-term memory (optional; llm/embedding required once enabled,
rerank/multimodal optional)
5. deep_research tool (optional; MiroThinker key + model)
6. Cold-start import from other AI tools (optional)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Off-by-one in the docstring: the next line is also numbered 6. (6. Done -> should be 7.), and line 1 still says "Six-step" while the list now has seven steps after Welcome.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ecd02a9. 6. Done changed to 7. Done.

Comment thread raven/cli/import_commands.py Outdated
from collections import Counter

from rich.progress_bar import ProgressBar
from rich.table import Table

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant: Table is already imported at module top (line 15). (The local time / Counter imports here are fine -- not imported at module level.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ecd02a9. Removed the local from rich.table import Table in status_cmd; module-level import is sufficient.

Kendrick-Song and others added 27 commits July 22, 2026 14:09
No breaking changes: all 12 import paths verified, 122 everos-specific
unit tests pass. Dependencies unchanged between versions.

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
…ore metadata

Introduce raven/importer/ module (Layer 1 of cold-start import) with:

- Platform, SourceKind, Tier enums for import scope modeling
- ImportMessage, ImportSession, ScanResult frozen dataclasses
- Scanner Protocol (async, with platform attribute)
- ImportState idempotent tracker (cached, atomic writes via atomic_replace,
  structured meta/entries layout, Counter-based summary)

Extend MemoryBackend.store() Protocol with optional metadata kwarg so
callers can pass backend-specific fields (app_id, project_id, is_final)
without leaking adapter concerns into the Protocol.

EverosBackend consumes metadata to control flush timing and forward
app_id/project_id as named params to the internal adapter layer. The
HTTP adapter now correctly propagates scope fields to both the /add
and /flush endpoints.

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
Implement the first platform scanner that discovers and reads Claude
Code local data (CLAUDE.md, project memory files, conversation JSONL).

Scanner capabilities:
- scan(): discovers all importable units (memory files + conversations)
  with active-session filtering and subagent exclusion
- read(MEMORY_FILE): paragraph-based splitting, YAML frontmatter
  parsing, per-file language detection for bilingual intro generation,
  MEMORY.md index-first ordering, file boundary markers (intro + end)
  with session preamble/epilogue
- read(CONVERSATION): JSONL event extraction with isMeta/compact/error
  filtering, tool_use to OpenAI ToolCall format conversion, tool_call_id
  linkage preserved, content truncation at 10K chars, numeric timestamp
  support

Also adds tool_call_id field to ImportMessage (fixes missing linkage
between tool results and their originating tool calls in EverOS).

Includes design spec and 52 tests (synthetic + real-data validated
against 70 sessions and 48K messages with 0 errors).

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
Move parse_frontmatter, parse_iso_ts_ms, is_cjk, and CJK_RE from
scattered private implementations into a shared module. Update callers:

- raven/importer/scanners/claude_code.py: remove local copies, import
  from raven.utils.text
- raven/channels/adapters/mochat/parsing.py: delegate to shared
  parse_iso_ts_ms (preserving string-only contract for mochat)
- raven/proactive_engine/sentinel/predictor/routine_learner.py: replace
  inline CJK regex with shared CJK_RE constant

raven/memory_engine/skill_local/registry.py is intentionally NOT
changed -- its frontmatter parser handles a non-standard format where
metadata values are inline JSON strings, which yaml.safe_load would
parse differently.

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
Co-authored-by: Claude (claude-sonnet-5) <noreply@anthropic.com>
Rename the importerror dataclass to importfailure across the importer module
to avoid shadowing python's built-in importerror exception. Update all references
in code and documentation.

Co-authored-by: Claude (claude-haiku-4-5) <noreply@anthropic.com>
EverosBackend.store() caught and logged every exception from the
adapter, which violates the MemoryBackend Protocol contract. Callers
(AgentLoop) already wrap store() in their own try/except, so let the
exception bubble up to them instead of swallowing it here.

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
Co-authored-by: Claude (claude-sonnet-5) <noreply@anthropic.com>
Adds the cold-start import CLI on top of the Task 1 orchestrator:
scan previews sources across platforms, run drives an interactive
(or --platform/--tier/--yes non-interactive) import with progress
reporting, and status reports ImportState counts as text or JSON.

Fixes a config-object mismatch versus the original plan: the memory
backend and plugin registry require RavenConfig (raven.config.raven),
not the base Config from raven.config.loader, which has no plugins/
memory fields.

Co-authored-by: Claude (claude-sonnet-5) <noreply@anthropic.com>
…st code

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
Co-authored-by: Claude (claude-sonnet-5) <noreply@anthropic.com>
Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
- onboard step 5: platform selection with display names and coming-soon
  entries, tier selection with back option and descriptions, everos
  memory check gate, asyncio.run nesting fix for questionary
- import logging: redirect_loguru_to_file for full lifecycle coverage
  (scan through execution), debug store request/message logging in
  orchestrator, log path displayed in summary
- onboard _memory_enabled and _config_everos_role: check api_key in
  addition to model to avoid false already-configured on fresh installs

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
…try points

gateway and agent commands now use redirect_terminal_fds_to_file to
capture everos embedded runtime structlog output (which writes directly
to stdout via printlogger, bypassing loguru) into their respective log
files. import and onboard step 5 already have this via the previous
commit. tui already had it. this completes coverage for all cli entry
points that start the everos embedded runtime.

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
Co-authored-by: Claude (claude-sonnet-5) <noreply@anthropic.com>
delete _RealEverosAdapter, embedded lifespan management, lancedb
migration, mode branching. EverosBackend always uses _HttpEverosAdapter.
start() calls ensure_everos_server() to auto-start the http server.
default base_url changed to localhost:18791. plugin manifest and
bootstrap config updated to remove mode/recall_method fields.

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
- _memory_enabled / _config_everos_role: check api_key not just model
- scan command: silence loguru during scan (clean output)
- http timeout: 10s to 360s (match everos session lock timeout)
- error logging: repr(e) fallback when str(e) is empty
- status command: remove unreliable state inference, show facts only
- stderr restore: level WARNING not DEBUG after import log redirect
- onboard step 5: full-width cjk punctuation, execution mode choice
  before confirm, summary with all selected options, background option

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
Co-authored-by: Claude (claude-sonnet-5) <noreply@anthropic.com>
Co-authored-by: Claude (claude-sonnet-5) <noreply@anthropic.com>
- Remove embedded-mode residuals from backend.py start() (rebase brought
  back _mode/_try_make_real_adapter from main's newer embedded code)
- Mock ensure_everos_server in test_start_stop_idempotent (new test from
  main, our HTTP-only backend needs mock)
- Restore sender_id/app_id/project_id assertion fixes in e2e tests
  (rebase reverted our earlier corrections)

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
- Add _match_everos_default() to fuzzy-match recommended model names
  against the fetched model list (handles provider prefixes)
- Pre-fill autocomplete with the recommended model (gpt-4.1-mini,
  Qwen3-Embedding-4B, etc.) so users can press Enter to accept
- Add "raven --help" to the onboard next-steps panel

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
- Fix _TOTAL_STEPS 5 -> 6 and import step headers 5 -> 6 to match
  the six-step wizard (deep_research + import)
- Fix ProgressEvent.error using str(e) while ImportFailure.error used
  repr(e) for the same exception -- now both use err_msg consistently
- Catch ensure_everos_server failures in EverosBackend.start() and
  degrade to NoOpAdapter instead of crashing with a raw traceback

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
- Move scan_all() to scanners/__init__.py with asyncio.gather for
  concurrent multi-scanner support
- Move filter_by_tier() to types.py alongside Tier definition
- Move build_scanners() registry to scanners/__init__.py (done earlier,
  now CLI _build_scanners wrapper removed entirely)
- Replace hardcoded ~/.raven/ in ImportState with config/paths.get_data_dir()
- Extract _pick(tpl, cjk) helper in claude_code scanner (5 call sites)
- Cache file reads in _read_project_memory to avoid double I/O

CLI layer is now a thin shell: parse args, call domain functions, render.

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
These spec/plan files are working artifacts from the brainstorming
and planning skills, not repository documentation.

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
- Move _everos_server.py from raven/cli/ to raven/plugin/memory/everos/
  _server.py to fix plugin->cli layering violation (CR #1)
- Split HTTP timeout: client default 60s (recall/health), memorize
  add/flush per-request 360s (CR #2)
- Defer backend.start() to post-handshake background task in TUI so
  first render is not blocked by server startup (CR #3)
- Remove unused app_id/project_id from ImportSession and scanner
  (CR #4)
- Add note on per-source-unit checkpoint granularity (CR #5)
- Add fcntl file lock to prevent concurrent server spawn race (CR #6)
- Fix loguru %s format to {} in state.py (CR #7)
- Fix docstring step numbering (CR #8)
- Remove redundant Table import in status_cmd (CR #9)

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
@Kendrick-Song
Kendrick-Song force-pushed the feat/cold_start_import branch from ecd02a9 to 5b2fe81 Compare July 22, 2026 06:09
Kendrick-Song and others added 2 commits July 22, 2026 16:30
…er spawn lock

fcntl is POSIX-only; importing it at module top crashes on Windows with
ModuleNotFoundError. Use raven.utils.portable_lock.file_lock (portalocker-
backed, POSIX fcntl + Windows LockFileEx) instead.

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
EverOS server (everos/core/persistence/locking.py) imports fcntl which
is POSIX-only. On native Windows:

- onboard step 4: detect win32, warn user, skip memory config
- backend.start(): detect win32, print Rich warning to stderr,
  degrade to NoOpAdapter instead of crashing

Both paths guide users to WSL for full memory support.

Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
@0xKT
0xKT merged commit ef24d21 into main Jul 22, 2026
8 checks passed
@0xKT
0xKT deleted the feat/cold_start_import branch July 22, 2026 09:09
0xKT added a commit that referenced this pull request Jul 22, 2026
## Summary

Bump version from 0.1.7 to 0.1.8. Release commit only touches
`pyproject.toml` and `uv.lock` (kept in sync via `uv lock`).

Changes bundled into this release since v0.1.7:

- feat(importer): cold-start import from other AI tools with EverOS HTTP
backend (#170)
- fix(build): pin locked dependency versions in installers (#185)
- fix(cli): stop blank api key exiting deep_research prompt (#184)
- fix: use redirect-free install.ps1 url on windows powershell 5.1
(#182)
- fix(tui): brighten selection highlight for readability (#175)
- fix(commitlint): align subject-case with conventional-commits standard
(#187)

## Type

- [x] Other

## Verification

- [x] Relevant lint / type checks pass locally

```
python3 scripts/check_commit_messages.py origin/main..HEAD  # exit 0
git diff --stat  # pyproject.toml + uv.lock, 1 line each
```

## Risk

- [x] Backward compatibility considered

Version bump only; no source changes.

## Related Issues

N/A

Co-authored-by: Claude (claude-opus-4-8) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants