Skip to content

Pr 12#18

Closed
danielhertz1999-bit wants to merge 15 commits into
CodeAbra:mainfrom
danielhertz1999-bit:pr-12
Closed

Pr 12#18
danielhertz1999-bit wants to merge 15 commits into
CodeAbra:mainfrom
danielhertz1999-bit:pr-12

Conversation

@danielhertz1999-bit

@danielhertz1999-bit danielhertz1999-bit commented Jun 20, 2026

Copy link
Copy Markdown

Summary

Type of change

  • Bug fix
  • New feature
  • Refactor (no behaviour change)
  • Documentation
  • Build / tooling

Affected areas

  • Capture path
  • Recall / retrieval
  • Consolidation / sleep cycles
  • Daemon lifecycle / FSM
  • Storage / encryption at rest
  • MCP wrapper (TypeScript)
  • Bench harness
  • CLI / doctor
  • Other: ___

Testing

  • pytest passes locally
  • ruff check src/ tests/ clean
  • New tests added for changed behaviour, or rationale below for why not

Benchmarks

If this PR touches retrieval, capture, or consolidation, include before/after numbers from the relevant bench.* harness.

  • Bench command run: python -m bench.___
  • Before:
  • After:

Notes for reviewers

claude and others added 15 commits June 17, 2026 21:11
The Rust hf-hub client uses a different TLS stack than Python and fails
to reach huggingface.co in containers with custom CA certificates
(UnknownIssuer). Add _auto_set_embed_offline() which runs at daemon
startup: if the bge-small-en-v1.5 snapshot directory already exists in
the HF cache, it sets IAI_MCP_EMBED_OFFLINE=1 automatically so the
embedder skips the network download entirely.

To seed the cache in a restricted environment, download the three model
files (model.safetensors, tokenizer.json, config.json) via Python's SSL
stack and place them under:
~/.cache/huggingface/hub/models--BAAI--bge-small-en-v1.5/snapshots/<rev>/

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mf3VFyVtczcK2WxKKCyBS4
…tdfl63

Auto-detect HF model cache to bypass Rust TLS in restricted environments
Introduces src/iai_mcp/_ipc.py as the central abstraction for all
daemon socket communication. On POSIX it delegates to the existing
Unix-domain socket; on Windows it uses TCP loopback with the ephemeral
port persisted in ~/.iai-mcp/.daemon.port.

Replaces all nine raw asyncio.open_unix_connection /
asyncio.start_unix_server / socket.AF_UNIX call-sites with the new
open_ipc_connection / start_ipc_server / make_sync_ipc_socket helpers.
The POSIX code-paths are structurally unchanged.

This is step 1 of the Windows port. Remaining blockers (fcntl, resource
module, POSIX signals, shell hooks, daemon installer) are tracked in the
audit at the top of _ipc.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add _filelock.py: on POSIX delegates to fcntl.flock; on Windows uses
msvcrt.locking with EWOULDBLOCK normalisation so all non-blocking callers
work unchanged.  Also changes doctor check_c to open the lock file with
O_RDWR (msvcrt.locking requires write access; harmless on POSIX).

Updated 7 files: capture_queue, lifecycle_event_log, lifecycle,
lock_protocol, hippo/_db, hippo/__init__ (dead import), doctor/_lifecycle_checks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- resource module: lazy-import behind platform check (Windows has no
  RLIMIT_NOFILE).
- Signals: build shutdown-signal list dynamically using hasattr; replace
  bare signal.SIGKILL with hasattr guard + sys.exit(1) fallback in
  _self_kill; use getattr for SIGTERM/SIGKILL in CLI stop and doctor
  orphan killer.
- os.getuid(): guarded with hasattr fallback to 0 (4 sites in _daemon.py).
- Log path: add _get_daemon_log_path() returning platform-appropriate
  path (%APPDATA%/iai-mcp/logs on Windows); add Windows branch to
  cmd_daemon_logs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- crypto.py: skip POSIX mode/uid checks on Windows (chmod is no-op);
  add _secure_key_file() using icacls for Windows ACL lockdown; guard
  os.fchmod with hasattr.
- cli/_crypto.py: guard st.st_uid and os.geteuid() for status report.
- memory_bank.py: guard os.fchmod call with hasattr.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds _is_windows() and SCHTASKS_TASK_NAME to cli/__init__.py and
implements cmd_daemon_install/uninstall/start/stop branches for Windows
in cli/_daemon.py using schtasks.exe and _render_schtasks_xml().

Also adds the Windows PowerShell turn-capture hook stub and the
WINDOWS_PORT_HANDOFF.md guide for continuing the port in a new session
(covers the remaining Steps 6-10 with file paths, code examples, and
test commands).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Creates three PowerShell scripts (.ps1) to replace shell hooks on Windows:
- iai-mcp-turn-capture.ps1: per-turn ambient capture (UserPromptSubmit)
- iai-mcp-session-capture.ps1: batch-capture at session end (Stop)
- iai-mcp-session-recall.ps1: session-start recall injection (SessionStart)

Updates src/iai_mcp/cli/_capture.py:
- _hook_ext() detects platform and returns '.ps1' on Windows, '.sh' on POSIX
- _capture_hook_paths(), _turn_hook_paths(), _session_recall_hook_paths()
  now use _hook_ext() for dynamic extension
- cmd_capture_hooks_install() uses 'powershell -ExecutionPolicy Bypass -File'
  for Windows hooks instead of 'bash' on POSIX
- Markers (_CAPTURE_HOOK_MARKER, etc.) changed to base names (no extension)
  so substring matching works for both .sh and .ps1 in settings.json

Updates pyproject.toml:
- Adds "_deploy/hooks/*.ps1" to package.data so PowerShell hooks are bundled

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
The bench scripts measured peak RSS via resource.getrusage(RUSAGE_SELF).ru_maxrss,
which is POSIX-only and crashes on import on Windows. Switch the Windows branch
to psutil.Process().memory_info().peak_wset; POSIX paths are unchanged.

- bench/memory_footprint.py: _rss_mb() guards on sys.platform == "win32"
- bench/memorygraph_memory.py: rss_mb() same pattern
- bench/consolidation_rss_peak.py: defer `import resource` into _ru_maxrss_bytes()
- bench/embed_warm_cost.py: rewrite _PAYLOAD_RSS subprocess template to detect
  platform and pick peak_wset / ru_maxrss; emit rss_platform key; measure_rss
  decodes the new field for the unit label, returns rss_platform in result dict

psutil is already a declared dependency (pyproject.toml), so no new package.
Verified end-to-end on Windows: all three helpers and the rewritten payload
produce sane values; AST-parse clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The fcntl→_filelock rewrite in commit 8154b9b swept timedelta and timezone
into the new _filelock import line, but those belong to datetime. The module
crashed with ImportError on any platform — masked until now because the
Windows porting work hadn't tried to import the chain end-to-end.

Move timedelta and timezone back to the `from datetime import` line.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Step 5 added Task Scheduler / schtasks backends to install/uninstall/start/
stop/logs, but the argparse help strings still mentioned only launchd and
systemd. Update them to list all three platforms.

The `Windows %APPDATA%\iai-mcp\logs` reference in the logs help is escaped
as `%%APPDATA%%` so argparse's own %-formatter doesn't choke on it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a new "Verified on Windows in-situ" section recording what was actually
exercised on a Windows machine in this session: AST parse over all 23
touched files, import smoke test of 10 runtime modules, CLI help, daemon
install --dry-run producing a valid Task Scheduler XML, and capture-hooks
status detecting the .ps1 templates. Also record the lifecycle_event_log
fix and daemon help-text update commits alongside the existing Step-7
entry.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…m shims

5 test modules imported fcntl or resource at top level, aborting pytest
collection before any test ran on Windows. Switch to the existing
_filelock shim (LOCK_EX/SH/UN/NB + flock) and guard the resource import
with a sys.platform check, skipping TestRaiseFdLimitClampsToHard on
Windows (resource.RLIM_INFINITY is unavailable there).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs from @warplayer's Windows 11 test report:

1. _filelock.flock() was calling os.lseek(fd, 0, SEEK_SET) before
   msvcrt.locking(), which silently moved the file offset. fcntl.flock
   on POSIX never touches the offset; callers that need a specific byte
   already seek themselves. Removed the lseek to match POSIX behaviour.

2. _patch_claude_desktop_config() called _build_iai_mcp_server_entry()
   without a FileNotFoundError guard in two places (new-file creation
   and existing-file update). If the mcp-wrapper wasn't built yet and
   Claude Desktop was installed, the function would crash after already
   writing hooks and settings.json, leaving a partial install. Both call
   sites now catch FileNotFoundError and return an actionable skip
   message (consistent with the guard already on the Claude Code path).

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mf3VFyVtczcK2WxKKCyBS4
@CodeAbra

Copy link
Copy Markdown
Owner

Huge thanks for taking this on — a Windows port is a big, cross-cutting piece of work and it means a lot that you're pushing on it. I'm following your commits and I want to land it once the port is complete (the PowerShell hooks wired up, the install/uninstall scripts, and ideally a run on a real Windows box to confirm it works end to end). No rush at all — take the time it needs, and I'll merge it as soon as it's ready. Genuinely appreciate the effort here.

@danielhertz1999-bit

Copy link
Copy Markdown
Author

Closing in favor of #12, which is the canonical, complete Windows port (head = danielhertz1999-bit:main). This branch is ~84 commits behind and predates the finished work — the full daemon/socket test port, the auth-token handshake, and several production fixes all landed on main and are reflected in #12. Consolidating to a single PR so there's one thing to review/merge. Thanks!

🤖 Addressed by Claude Code

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