Skip to content

History

Revisions

  • audit F01: bound capture pipeline stop() with timeout Closes the highest-blast-radius shutdown hang flagged in audit Phase 1. runtime_daemon.stop() awaited self._capture_pipeline.stop() with no upper bound. A disconnected USB webcam or stuck mediapipe worker can block this close indefinitely; only SIGKILL unblocks the daemon, and SIGKILL leaves the AVFoundation camera handle owned by a dead PID — the next daemon launch then fails the camera-acquire dance and the user is stuck in a permission loop. Fix: wrap the call in asyncio.wait_for(..., timeout=5.0). On timeout, log an explicit error and proceed with the rest of the shutdown chain (input hooks stop, session report write, WS server stop, etc.). The kernel reclaims the camera handle on actual process exit. The previous try/except: pass swallowed every non-timeout error; replaced with an exception-logged variant so adapter-level failures surface. Test: cortex/tests/unit/test_capture_stop_timeout.py. 3 cases. A _NeverFinishingPipeline confirms the timeout fires within bounds; a fast pipeline is not interrupted; non-timeout exceptions propagate. All fail on main (the code uses await with no wait_for, so the hung-pipeline test there would itself hang — the wrapper-pattern tests prove the new contract). Compatibility: behavioural change at shutdown. Previously infinite wait; now 5 s budget. Legitimate close paths complete in well under 1 s; 5 s is generous. No wire/schema impact. Rollback: git revert is clean. Single hunk in runtime_daemon.py.

    @StevenWang-CY StevenWang-CY committed May 18, 2026
    5828fa7
  • audit F03: track and drain background asyncio tasks on shutdown Closes the orphan-task leak flagged in audit Phase 1. The state loop's intervention dispatch (runtime_daemon.py:1057) used bare asyncio.create_task with no reference. stop() cancelled only the long-running loops in self._tasks; any in-flight intervention task was orphaned. If that task held a file handle (session record, baseline), the daemon could exit mid-write and truncate the JSONL. - New self._background_tasks: set[asyncio.Task] alongside self._tasks. - New _spawn_background_task(coro, *, name=...) helper: adds to the set, registers add_done_callback(self._background_tasks.discard) so completion auto-prunes — the set never grows beyond what is actually running. - Orphan call site rewritten to use the helper. - stop() cancels every outstanding background task and awaits them with return_exceptions=True before clearing. Test: cortex/tests/unit/test_background_task_tracking.py. 4 cases on a _StubDaemon that carries the same _background_tasks set + helper but no camera/store dependencies (the full CortexDaemon needs both to boot, and the contract under test is just the helper + stop drain). Asserts: spawn tracks, completed tasks auto-discard, cancel + drain on stop, multiple concurrent tasks all drain. All fail on main (helper does not exist). Compatibility: additive. self._tasks behaviour unchanged; no wire or schema impact. Rollback: git revert is clean. The orphan call site reverts to bare asyncio.create_task; the helper + set die with the diff.

    @StevenWang-CY StevenWang-CY committed May 18, 2026
    f0b95b0
  • audit F02: atomic session report write at shutdown Closes the silent-session-loss failure flagged in audit Phase 1: the old shutdown path wrapped both report computation and the file write in a single try/except. Disk-full, permission errors, or a crash mid-write would log a warning and leave nothing on disk — and the prior session file was already overwritten by then. - cortex/libs/utils/atomic_write.py: atomic_write_text and atomic_write_json write to <path>.tmp, fsync the descriptor, then os.replace into place. os.replace is atomic on POSIX and NTFS; any failure before the rename leaves the destination unchanged. - runtime_daemon.stop(): split compute-vs-disk error handling. finish() errors log "nothing to persist" and skip the write. Disk errors log "prior file preserved" — and because the rename never happened, the previous on-disk report (if any) survives. Both paths log at ERROR so a missing report is observable at default level. Test: cortex/tests/unit/test_atomic_write.py. 5 cases — JSON round trip, no leftover .tmp on success, prior contents survive simulated PermissionError on os.replace, tmp file cleaned up on simulated mid-write OSError. All fail on main (helper module did not exist). Regression check: full unit suite (931 tests) passes. Compatibility: additive. On-disk session_<id>.json format unchanged. No migration; no client coordination. Rollback: git revert is clean. The helper has only one caller; the prior write_text path is restored straight-line.

    @StevenWang-CY StevenWang-CY committed May 18, 2026
    7169750
  • audit F08+F07b: capability token on launcher /stop, native-host token fetch Closes the second half of the cross-origin-localhost CSRF gap. F07 gated WS SHUTDOWN; this commit gates the launcher agent's POST /stop and adds the native-host primitive legitimate clients need to acquire the token. - launcher_agent.py: POST /stop now requires X-Cortex-Auth-Token. The launcher's "zero cortex imports" invariant (docstring) is preserved by inlining a minimal path resolver + hmac.compare_digest. /launch, /health, /status stay open — those are non-destructive and the supervisor liveness probe depends on /health. - native_host.py: new get_auth_token command. Loads (or creates) the token via cortex.libs.auth and returns it. The browser <-> native host channel is already OS-authenticated per-profile so this does not widen the attack surface; mode-0600 file remains unreachable from any sandboxed page context. Test: cortex/tests/unit/test_launcher_auth.py + test_native_host_auth.py. The launcher tests boot LauncherHandler on an ephemeral port and monkeypatch _stop_daemon to a no-op so they don't kill the developer's running daemon. Cases: 401 without token, 401 with wrong token, 200 with correct token, /health stays open, fall-closed when token file missing (no open-by-default failure mode). Native-host tests verify get_auth_token returns existing tokens unchanged and provisions when absent. 7 cases. All fail on main. Compatibility: breaking for any external POST /stop caller without the token. Internal: background.ts:2578-2583. After this commit Step 6 of the extension's stop chain fails 401; Steps 2-5 (HTTP /shutdown, native messaging) still complete the kill. Restoring Step 6 needs the extension to fetch the token via the new native-host command — split out as F08b (gated on F40 TS test infrastructure). Rollback: git revert is clean. Launcher's inline auth helper is self-contained; native-host command has no side effects.

    @StevenWang-CY StevenWang-CY committed May 18, 2026
    65d110a
  • audit F07: capability token gate on WebSocket SHUTDOWN Closes the local-CSRF hole flagged in audit Phase 1: any localhost origin (malicious webpage in another tab, hostile extension on the same browser profile) could connect to ws://127.0.0.1:9473 and send a SHUTDOWN message to kill the daemon. The fix is tactical mitigation of Architectural Debt #2 (implicit "localhost = trusted user" model); the full client-bootstrap rework remains deferred for its own design doc. - cortex/libs/auth/local_token.py: generates a 256-bit secret on first daemon start, persists at <config_dir>/auth.token with mode 0600 via atomic-write (tmp + chmod + rename). Reused across restarts. - verify_token() uses secrets.compare_digest; never raises; returns False for any of missing/empty/wrong/unreadable. - WebSocket SHUTDOWN handler now requires payload.auth_token to match. Reject path logs the client_id and returns silently — no information leakage to probing callers, no exception propagated. - runtime_daemon.start() provisions the token before any service binds. The legitimate user's stop-Cortex flow has 6 redundant steps; only Step 1 (WS SHUTDOWN) is gated by this change. Steps 2-6 (HTTP /shutdown, native messaging, launcher /stop, tab cleanup) still run and reliably stop the daemon. Restoring Step 1 needs a native-host-mediated token fetch — filed as F07b in audit/execution-log.md (bundled into F08 since the same primitive serves both gates). Test: cortex/tests/unit/test_auth_local_token.py — 8 cases. Asserts idempotent provisioning, 0o600 permission bits, constant-time compare, truncated-file replacement, and crucially that the WS server's shutdown callback fires for a correct token and does not fire for a missing token. All fail on main (module does not exist; SHUTDOWN handler accepts unauthenticated messages). Compatibility: breaking for any external WS client that sends SHUTDOWN without auth_token. Internal: only background.ts:2548. After this commit, Step 1 of its stop chain is a silent no-op; user-facing function preserved by Steps 2-6. Rollback: git revert is clean. Token file is harmless to leave behind. Threat model: closes cross-origin-localhost. Does not (and cannot) close malware-as-the-user or a debugger attached to the daemon.

    @StevenWang-CY StevenWang-CY committed May 18, 2026
    3268a04
  • audit F19: thread correlation IDs UI -> daemon -> LLM Closes the maintainability/correctness root rot identified in audit Phase 1: a single user action could not be traced from API call through state engine through LLM call back to the response without grep+wallclock alignment across four log streams. - New cortex/libs/logging/correlation.py: ContextVar-backed id, scope manager, stdlib Filter that injects record.correlation_id, helper to install the filter idempotently. - structlog processor chain now includes merge_contextvars so any get_logger()-emitted record carries correlation_id automatically. - FastAPI middleware mints (or accepts via X-Cortex-Request-ID) one id per request, binds it for the lifetime of dispatch, echoes it back on the response, and exposes the header through CORS. - WebSocketServer enters a correlation scope around every inbound message; _broadcast stamps the active id onto outbound messages with no correlation_id of their own, so daemon-initiated traffic stays traceable to the originating request. - Anthropic planner's llm.request status=ok log line now includes the active correlation id so the next finding (F20 cost telemetry) can group spend per request without retrofit. Test: cortex/tests/integration/test_correlation_ids.py — 8 cases. All fail on main (ModuleNotFoundError on the new module, missing middleware header, no broadcast stamping). All pass on this branch. Compatibility: additive. WSMessage.correlation_id already existed and was optional. No schema migration, no client coordination required. The TS extension half of the chain remains open as new Ledger entry F19b (gated on F40 TS test infra). Rollback: git revert is clean — code-only change, no persisted state. Also writes audit/findings.md (56-finding Ledger + Cheap Wins + Architectural Debt), audit/state.md (Phase 2 pointer), and seeds audit/execution-log.md with this commit's entry.

    @StevenWang-CY StevenWang-CY committed May 18, 2026
    6eca4c1
  • fix: scope card stylesheets, restore window drag, fix status icon + onboarding layout - Scope QFrame card stylesheets to objectName so background/border/radius no longer leak into every QLabel descendant (was scrambling text in connections/dashboard cards). - Drop NSWindowStyleMaskFullSizeContentView and enable setMovableByWindowBackground_ so the title bar keeps a real drag region and stays grabbable. - Stop reparenting Qt's contentView under an NSVisualEffectView (orphaned the window and bounced the Dock icon); tint windowBackgroundColor instead for the unified-titlebar look. - Render status-bar icon as a unicode glyph instead of an SF Symbol that came back invisible inside the ad-hoc-signed PyInstaller bundle. - Onboarding: grow default size, wrap content in a scroll area, center on first show, and poll camera/accessibility permission state every 1.5s so the pills flip without a relaunch. - Dashboard: center window on first show to avoid stale multi-monitor geometry stranding it off-screen.

    @StevenWang-CY StevenWang-CY committed May 18, 2026
    2030f3d
  • refactor: macOS-native UI across all surfaces + skill-driven audit fixes UI refactor (preserves brand identity — terracotta accent, Cormorant Garamond wordmark, ECG heartbeat motif): * New shared design system: cortex/libs/design/tokens.yaml as source of truth, emitted via cortex/scripts/sync_design_tokens.py to all three surfaces (desktop_shell/tokens.py, browser_extension/design-tokens.ts, vscode_extension/src/design-tokens.ts). * New AppKit bridge: cortex/apps/desktop_shell/mac_native.py wraps pyobjc for NSVisualEffectView vibrancy, unified title bar, system font (SF Pro), effective-appearance detection, and a real NSStatusItem with SF Symbol heart for the menu bar. * Desktop shell: dashboard, settings, onboarding, overlay, connections, and tray all migrated to system semantic palette + SF system fonts + HIG spacing & radii. Drop shadows removed; hairline separators preserved. Tabs replaced with capsule segmented control. Overlay uses HUD vibrancy. Brand wordmark (Cormorant italic) and hero numerics preserved. * Browser extension: page-reset.css adds prefers-color-scheme dark variant; Inter font dropped in favor of the SF system stack; background.ts tears down reconnect/persist timers on chrome.runtime.onSuspend. * VS Code panel: hardcoded indigo replaced with VS Code theme variables so the panel adopts the user's editor theme; brand terracotta survives only on the intervention-card left border and the breathing-pacer ring. ws-client reconnect now uses AbortController. Skill-driven audit fixes (swift-concurrency-pro, swift-testing-pro, swiftdata-pro principles transferred to Python/TS idioms): * schemas/state.py: drop dead HYPO_APNEA enum value (defined but excluded from every downstream Literal); SignalQuality.overall/acceptable promoted from @property to @computed_field so Pydantic v2 model_dump actually serialises them onto STATE_UPDATE. * api_gateway/routes.py: /shutdown now declares a ShutdownResponse model. * runtime_daemon: INTERVENTION_APPLIED ack deduplicated by (intervention_id, phase) to prevent overwrite-on-retry. * llm_engine/anthropic_planner.py: messages.create wrapped in asyncio.shield so cancellation can't leave the Bedrock HTTP connection half-open. * janitor/retention.py: *.tmp files older than 24h are tombstoned regardless of directory retention window. * desktop_shell/main.py: _connect_loop re-raises CancelledError instead of swallowing it. * intervention_engine/executor.py: two # type: ignore lines now carry inline rationale. Frontend audit fixes: * popup.tsx: empty-goal submit no longer silently substitutes "Study session"; START_FOCUS is gated on trimmed non-empty input. Goal input capped at 120 chars. briefing.left_off_at Resume button equally gated. * background.ts: 8 production console.log/error calls now gated behind const DEBUG = false (page-context overlay logs replaced by comments since they can't see the service-worker scope). * panel-provider.ts: STATE_COLORS now sourced from the emitted PANEL_STATE_HEX_LIGHT design token; micro_steps cast replaced with an Array.isArray + string type guard. * onboarding.py: Bedrock token length sanity check (warn under 20 chars). Infrastructure / versioning: * browser_extension and vscode_extension package.json bumped to 0.2.1 (matched pyproject.toml). cortex.spec now reads version dynamically; connections.py + build_macos_app.sh use a vsix glob so future version bumps cascade automatically. Tests: * New: tests/conftest.py (reusable mock_pyside6 fixture), tests/unit/test_mac_native.py (AppKit-mocked, runs on any platform). * test_desktop_shell mocks extended for QPointF, QButtonGroup, QStackedWidget, QPainter.drawPath, QPainterPath.cubicTo, QAction shortcuts, QPushButton.setCheckable. test_schemas_v2 updated for the HYPO_APNEA removal. Verification: pytest 1030 passed + 3 skipped, ruff clean, browser + vscode tsc clean. No mock, stub, or placeholder remains in any production path.

    @StevenWang-CY StevenWang-CY committed May 14, 2026
    875db14
  • fix: close v0.2.1 audit follow-ups (stop button, ack protocol, cooldowns, BYOK plumbing) A second-pass multi-agent audit of the v0.2.1 release identified six P0/P1 gaps that prevented the "100% completeness" claim from being defensible. This commit closes all of them. Stop button (P0, both modes): - WebSocketBridge.send_shutdown(): emit a top-level {"type":"SHUTDOWN"} message which the daemon's WS server routes to _request_shutdown + SIGTERM. The previous send_user_action("shutdown", "") was silently dropped because USER_ACTION needs an intervention_id. - main.py _request_remote_shutdown now uses send_shutdown and waits 1s before quitting Qt (was 500ms) so the daemon has time to release the camera and flush the WebSocket. - controller.py (DMG-ship in-process path) now connects dashboard.stop_requested to a new _stop_daemon_and_quit method that schedules daemon.stop() on the daemon thread loop, waits up to 5s for the camera/SessionReport finalisation, then quits Qt. Previously the controller did not subscribe to this signal — the fix in main.py only affected dev mode. DMG-mode controller wiring (P0): - controller.py also connects onboarding.extensions_requested -> _show_connections and dashboard.goal_set -> _on_goal_set. Without these the v0.2.1 "Open Connections" button on the wizard's step 4 was dead in DMG mode (the fix only lived in main.py / WS dev mode). LLM backend selector (P0): - runtime_daemon.apply_settings now accepts both "llm_provider" (canonical) and "llm_mode" (legacy key the SettingsDialog emits). The dropdown was silently a no-op before because of the key mismatch. UIPlan.max_visible_lines (P0): - Added max_visible_lines (default 40) to UIPlan in libs/schemas/intervention.py. AnthropicPlanner now copies constraints.max_visible_lines / 2 onto plan.ui_plan after enrichment. VS Code's handleIntervention already reads ui_plan.max_visible_lines; previously the value was always undefined and the hard-coded 20-line fallback was always used. Cooldown 30x shrink (P0): - New InterventionConfig.intervention_dismiss_cooldown_ms (30 min) and url_dismiss_cooldown_ms (10 min). Daemon's apply_settings reads these dedicated fields when broadcasting SETTINGS_SYNC. Previously it reused cooldown_seconds * 1000 (60s) for both, which compressed the browser-extension defaults by 30x the moment the daemon connected — dismissed interventions could re-fire after 60 seconds. Browser extension INTERVENTION_APPLIED ack (P0): - background.ts now sends INTERVENTION_APPLIED after handleIntervention (phase=apply) and handleRestore (phase=restore) with truthful applied_actions / errors arrays. This was the largest contract gap in v0.2.1 — the browser is where >80% of mutations live (tab hides, overlay injections), but the daemon was getting acks only from VS Code, so InterventionOutcome.workspace_restored was largely theatrical. - New sendInterventionApplied helper in background.ts mirrors the ws-client.ts one in VS Code. VS Code restore ack (P1): - extension.ts onRestore now calls wsClient.sendInterventionApplied( intervention_id, "restore", success, applied, errors) after running restoreFoldState — daemon now sees real outcomes for both phases. ActivitySummarizer (P1): - services/activity_tracker/summarizer.py rewritten from a direct Azure OpenAI httpx call (config.azure.*, removed in v0.2.1) to the Anthropic SDK at the Haiku tier via build_anthropic_sdk_client + resolve_anthropic_model_id. The Azure path silently raised AttributeError, swallowed by the upstream try/except — the recap feature was running on the dumb template fallback permanently. Desktop overlay causal_explanation (P1): - overlay.py show_intervention now renders a "Why this?" line below the micro-steps when the daemon supplies a >20-char causal_explanation. VS Code and the browser popup already displayed this field; the desktop overlay was the only client silently dropping it. Stale legacy references (cleanup): - libs/config/defaults.yaml: replaced the legacy LLM block (mode: azure, remote: {ssh_tunnel...}, local: {ollama}, fallback_mode: local_ollama) with the new Bedrock-shaped schema. The mismatch was harmless at runtime (LLMConfig uses extra="ignore") but confused anyone reading the file. - services/api_gateway/routes.py: dropped the dead legacy service-key lookup (remote_qwen_client / local_ollama_client) — only "llm_client" is registered now. Verification: - pytest cortex/ : 1024 passed, 2 skipped, 0 failed. - ruff check . : clean.

    @StevenWang-CY StevenWang-CY committed May 12, 2026
    a3f85c7
  • release: v0.2.1 — close audit-flagged completeness gaps + Bedrock LLM migration Replaces the legacy Azure / remote Qwen / local Ollama / OpenAI-compat LLM backends with a single Anthropic SDK transport that defaults to AWS Bedrock. Closes the 28 gaps flagged by the v0.2.0 multi-agent audit, surfacing v0.2.0 fields the daemon already produced but never forwarded, fixing visible client bugs (breathing pacer, fold restore, stop button, camera onboarding), and wiring the previously-dead session_report, ML classifier, recovery credit, and retention sweep into production. LLM / transport (Bedrock primary): - New cortex.libs.llm.anthropic_client: build_anthropic_sdk_client + resolve_anthropic_model_id select between AsyncAnthropicBedrock, AsyncAnthropicVertex, and AsyncAnthropic via ANTHROPIC_PROVIDER. - New cortex.services.llm_engine.anthropic_planner.AnthropicPlanner: per-template model tier (Haiku / Sonnet / Opus), forced tool-use for structured InterventionPlan output, ephemeral prompt caching on the system block, bounded retry with jittered backoff, async semaphore concurrency cap, consecutive-failure circuit breaker, structured llm.request observability logs. - BYOK migrated to AWS Bedrock bearer token in macOS Keychain (cortex.bedrock / bearer_token); onboarding step 3 collects it with a region picker. Daemon startup surfaces the token via AWS_BEARER_TOKEN_BEDROCK. - Deleted azure_openai.py, remote_qwen.py, local_ollama.py, setup_ssh_tunnel.sh, run_llm_server.py. LLMConfig rewritten with legacy-value validators so 0.1.x .env files don't crash on first launch after upgrade. Detection / state engine: - Reverted StateEstimate.confidence to the raw smoothed dominant score so the spec's 0.85 trigger gate is mathematically reachable (softmax over 4 saturated scores capped at ~0.475). - PerUserLogisticClassifier now instantiates when state.ml_enabled and a serialized model exists; smoother blends ml_p_hyper into HYPER with alpha ramped by ml_min_labeled_episodes / ml_alpha_full_at_episodes. - StressIntegralTracker receives hrv_sigma from baselines.metric_distributions['hrv_rmssd'].std so the integral matches the AMIP safety-floor z-score scale. - apply_recovery_credit called on FLOW recovery in _handle_restore_updates. - mic_active / fullscreen_active sourced from cortex.libs.utils.receptivity (CoreAudio + Quartz) instead of being hard-coded False. - Unified the two hyper_dwell_seconds config fields; TriggerPolicy now reads StateConfig only. WebSocket contract: - _make_state_update payload adds stress_integral, calibrated_probabilities, classifier_source, classifier_alpha, ISO timestamp. _make_intervention_trigger adds causal_explanation, consent_level, plan_warnings — VS Code "Why this?" panel and popup transparency section are no longer starved. - New INTERVENTION_APPLIED ack: clients send {intervention_id, phase, success, applied_actions, errors} after apply / restore. Daemon updates Mutation.success so InterventionOutcome.workspace_restored is truthful. - Dedicated 500ms _broadcast_loop independent of the pipeline; previous cadence drifted to 2-3s during LLM trigger work. - broadcast_settings now emits intervention_dismiss_cooldown_ms / url_dismiss_cooldown_ms (W-16 daemon-side half). - Fixed parser.py:280 digit regex (r"\\d+..." -> r"\d+...") so LLM-grounded causal explanations are retained. VS Code extension: - panel-provider no longer rebuilds HTML on every STATE_UPDATE; host posts diff messages and the inlined script patches DOM in place, so the breathing pacer animation actually completes its 19-second cycle. - restoreFoldState sets editor.selection before each editor.fold; snapshot tracks the ranges we explicitly folded (instead of every foldable range), so dismiss leaves the file in its pre-intervention fold state. - COPILOT_THROTTLE emitted from daemon via send_message(..., target_client_types=["vscode"]); previously the VS Code handler was orphaned. - handleIntervention reads ui_plan.max_visible_lines instead of the hard-coded ±20-line window. - context-provider populates recent_edits from onDidChangeTextDocument (file/line/length/kind, privacy-preserving). - ws-client.sendInterventionApplied wired; extension.ts acks apply. Desktop shell: - Dashboard _stop_btn and _goal_input wired through new stop_requested / goal_set signals to _shutdown_daemon and USER_ACTION. - SettingsDialog persists every widget via QSettings("Cortex","Desktop") and reconciles SETTINGS_SYNC payloads via apply_payload(). LLM combo box rebuilt for Bedrock / Vertex / direct / rule_based. - Onboarding "Grant Camera Access" calls cortex.libs.utils.platform.request_camera_permission() so the AVFoundation prompt actually fires; falls back to System Settings on pyobjc-missing. - WS-mode CortexApp now wires show_connections_requested and the dashboard Connect button (in-process mode was already wired). - Onboarding step 4 has a real "Open Connections" button. - Legacy WS reconnect uses 3->30s exponential backoff matching the browser / VS Code clients. Browser extension: - tab-manager classifyTabType output collapsed to the 9-type Python vocabulary so daemon-side parser keeps the values. - TabInfo.last_activated_ago_seconds added to the schema. - newtab.tsx injects an inline <style> with html,body,#__plasmo{background:...} so the new-tab page no longer white-flashes before page-reset.css attaches. - Deleted orphan content.tsx (never auto-injected) and stale manifest.json (Plasmo authoritative). - W-06 regression fixed: build/create/implement/make/write removed from rabbit_hole.py stop_words; regression test added. Lifecycle + retention: - SessionReportGenerator wired into runtime_daemon: start in __init__, record_state/hr/hrv/stress in the state loop, finish() in stop() writes storage/sessions/session_<id>.json. - New cortex.services.janitor.retention.sweep_once runs daily; StorageConfig.{session,feature,error}_retention_days are finally enforced. Build & packaging: - build_macos_app.sh switched from grep denylist to an explicit allowlist for bundled .env keys, with a defence-in-depth grep that aborts the build if any forbidden pattern slips through. - pyproject.toml bumped to 0.2.1; cortex.spec reads CFBundleVersion dynamically via tomllib. - Dropped fakeredis (declared, never imported). - redis_store.py asserts converted to explicit raises so they survive python -O. - ruff check is 0 errors after auto-fix + targeted per-file-ignores for test patterns. Docs: - cortex/docs/apis.md aligned with the real /api/stress-integral and /api/helpfulness/summary payload shapes. Tests: - pytest cortex/ : 1024 passed, 2 skipped, 0 failed. - New tests/unit/test_anthropic_planner.py (15 tests covering tool-use extraction, model-tier routing, retry+jitter, circuit breaker, cache, fallback chain). - W-06 regression guard in test_rabbit_hole.py.

    @StevenWang-CY StevenWang-CY committed May 12, 2026
    c9e4883
  • feat: add LeetCode submission awareness and improve zombie detection robustness - Background: Implement LEETCODE_CONTEXT_UPDATE messaging and session cleanup - Runtime Daemon: Add LeetCode submission timestamp normalization and epoch-to-monotonic conversion - Zombie Detector: Refine detection logic to rely on other sensors when blink telemetry is unavailable - API Gateway: Enhance WebSocket server heartbeat and connectivity handling - Build: Update macOS application signing, DMG creation, and native host installation scripts - Evaluation: Normalize HRV/stress scoring and refine app classification logic

    @StevenWang-CY StevenWang-CY committed Apr 23, 2026
    aed7745
  • Harden DMG packaging and launch lifecycle reliability

    @StevenWang-CY StevenWang-CY committed Apr 23, 2026
    75209bd
  • fix: DMG bundling paths, cross-numpy HRV, learning-loop gaps Bundling (cortex/scripts/cortex.spec): - Drop the redundant `resources/` prefix so browser extensions, the VSIX and native_host.py land at the paths ConnectionsPanel resolves (Contents/Resources/browser_extension_chrome, etc.). The prior layout left "Connect Chrome/Edge/Editor" returning clipboard paths that did not exist on disk. - Ship native_host.py and install_native_host.py as on-disk resources so Chrome's native messaging host can invoke them directly. - Bundle the key-stripped .env under the real `.env` filename that _bundled_env_files() looks for inside MEIPASS. Start/stop (cortex/scripts/native_host.py, launcher_agent.py): - Restore a portable `#!/usr/bin/env python3` shebang (install_native_host patches it to an absolute Python at install time). - Prefer `open -a /Applications/Cortex.app` when the DMG is installed so the Start button in the browser extension works for users who never set up a dev checkout; dev mode still falls back to `python -m cortex.scripts.run_dev` via Terminal.app. Learning loop (cortex/services/runtime_daemon.py): - Call HelpfulnessTracker.record_user_action before end_tracking so the engaged / ignored / undone signal actually contributes to the reward instead of always sitting at zero weight. Signal processing / tests: - Add a compatibility shim in libs/signal/peak_detection.py so the LF/HF Lomb-Scargle path works on NumPy 1.x (np.trapz) and 2.x (np.trapezoid). - Update stress-integral tests to pin hrv_sigma=1.0 where they assert raw-ms integration, and add a dedicated test covering the new sigma-normalized (z-scored) default. Docs: - README: reflect the 55-file test suite and the DMG-aware Start/Stop behaviour in the troubleshooting section.

    @StevenWang-CY StevenWang-CY committed Apr 22, 2026
    713e00b
  • Refine Cortex 0.2.0 pipeline, safety, evaluation, and docs

    @StevenWang-CY StevenWang-CY committed Apr 22, 2026
    af3a25c
  • docs: update all documentation to reflect DMG installer as primary setup method

    @StevenWang-CY StevenWang-CY committed Apr 4, 2026
    63aa80f
  • fix: force dashboard window to front on launch; refined UI with card shadows and improved spacing

    @StevenWang-CY StevenWang-CY committed Apr 4, 2026
    1ef7aac
  • fix: ad-hoc signing without hardened runtime to fix Python.framework Team ID mismatch

    @StevenWang-CY StevenWang-CY committed Apr 4, 2026
    8f78267
  • fix: regenerate .icns with all Apple-required sizes; fix venv activation in build script

    @StevenWang-CY StevenWang-CY committed Apr 4, 2026
    2f319e8
  • feat: add native macOS desktop app with DMG installer Replace the 10-step manual setup with a single DMG download. The PySide6 desktop app runs the daemon in-process (shared TCC camera identity), shows a two-tab biometrics dashboard matching the browser extension popup design, and provides one-click connection for Chrome, Edge, and VS Code / Cursor / VSCodium. Architecture: - CortexAppController boots CortexDaemon in a background daemon thread - DaemonBridge relays state via Qt signals (copy.deepcopy for safety) - Capture pipeline isolated in dedicated thread (no asyncio blocking) - Graceful shutdown force-releases camera before async teardown New files: - controller.py — in-process daemon + Qt controller - tokens.py — design token port (warm palette, typography, spacing) - connections.py — browser/editor setup with translocation guard - cortex.spec — PyInstaller spec (MediaPipe data, keyring metadata) - cortex_entitlements.plist — camera + automation entitlements - build_macos_app.sh — full build pipeline (extensions → sign → DMG) Modified: - runtime_daemon.py — state/intervention callback hooks - settings.py (config) — bundled-mode paths, Keychain API key loading - dashboard.py — two-tab layout (consumer + advanced debug) - onboarding.py — 4-step wizard (camera, accessibility, BYOK, extensions) - overlay.py, settings.py (UI), tray.py — warm palette redesign - install_native_host.py — accepts project_root for bundled mode

    @StevenWang-CY StevenWang-CY committed Apr 4, 2026
    83a606c
  • chore: untrack dev-only files — CLAUDE.md, CHANGELOG.md, vscode compiled output

    @StevenWang-CY StevenWang-CY committed Mar 25, 2026
    6f3473a
  • Add full wiki: setup, architecture, how it works, API, troubleshooting, privacy

    @StevenWang-CY StevenWang-CY committed Mar 24, 2026
    422cbfd
  • chore: cache-bust banner SVG reference

    @StevenWang-CY StevenWang-CY committed Mar 24, 2026
    c1818e3
  • chore: update brand assets

    @StevenWang-CY StevenWang-CY committed Mar 24, 2026
    2858ed3
  • chore: update docs for camera fix; refresh extension assets and UI

    @StevenWang-CY StevenWang-CY committed Mar 24, 2026
    b8e156a
  • fix: always prefer Mac built-in camera; restrict Continuity Camera probes

    @StevenWang-CY StevenWang-CY committed Mar 24, 2026
    d3443cb
  • chore: public readiness — remove personal data, untrack artifacts, rewrite setup docs - Remove UPenn credentials from config defaults, .env.example, scripts - Remove hardcoded absolute paths from .cortex_launcher.c (now derives from executable location) - Reset native_host.py shebang to generic (patched at install time) - Untrack build artifacts: .plasmo/, CortexDaemon.app/, .vsix, IDE configs - Comprehensive .gitignore update - Fixed extension manifest key for deterministic ID (no --extension-id needed) - install_native_host auto-detects all Chromium browsers + existing extension IDs - Rewritten README with 7-step setup guide, 3 LLM options, troubleshooting - Rewritten setup.md with correct order and complete instructions - Fixed API endpoint docs, test counts, CHANGELOG

    @StevenWang-CY StevenWang-CY committed Mar 22, 2026
    5f4ac3d
  • chore: add CortexDaemon.app launcher source and update gitignore Add .cortex_launcher.c (compiled Mach-O source for CortexDaemon.app TCC camera access), ignore runtime artifact .cortex_launch.sh, and track edge manifest.

    @StevenWang-CY StevenWang-CY committed Mar 19, 2026
    d3afd82
  • feat: click-to-start daemon launch, comprehensive stop, smart camera selection - Native messaging host launches daemon via Terminal.app for macOS TCC camera access - Multi-step stop kill chain: WS shutdown → HTTP → SIGTERM (port+pgrep) → SIGKILL - Smart camera selection: always prefers MacBook built-in, skips iPhone Continuity Camera - Post-open camera re-verification catches dynamic device reordering race conditions - Explicit AVFoundation camera permission request triggers macOS dialog on first launch - Launcher agent (port 9471) as HTTP alternative to native messaging - CortexDaemon.app bundle with compiled Mach-O launcher and own TCC identity - Extension popup: Start/Stop Cortex with 3-path launch fallback - Updated both READMEs with current architecture and setup instructions

    @StevenWang-CY StevenWang-CY committed Mar 19, 2026
    32bb3ff
  • feat: visual identity refresh — design tokens, ambient engine, popup & newtab redesign Unified design system with warm-shifted canvas, reduced borders, new duration tokens. Simplified ambient particles to gentle floating dots, capped somatic filter at 2%. Redesigned newtab Pulse Room with HYPER dampening, reduced-motion support, and resume cards. Popup uses sentence case, progress bars, and single CTA pattern throughout. Bundled fonts via web_accessible_resources for offline use.

    @StevenWang-CY StevenWang-CY committed Mar 18, 2026
    6fcde2f
  • feat: visual identity refresh — design tokens, ambient engine, popup & newtab redesign Unified design system with warm-shifted canvas, reduced borders, new duration tokens. Simplified ambient particles to gentle floating dots, capped somatic filter at 2%. Redesigned newtab Pulse Room with HYPER dampening, reduced-motion support, and resume cards. Popup uses sentence case, progress bars, and single CTA pattern throughout. Bundled fonts via web_accessible_resources for offline use.

    @StevenWang-CY StevenWang-CY committed Mar 18, 2026
    742bd12