89 lines
4 KiB
Markdown
89 lines
4 KiB
Markdown
# iCalls (`ICallsSync`)
|
|
|
|
## Purpose
|
|
|
|
Ingest macOS call history (Phone/FaceTime) into the server for timelines,
|
|
recent activity, and correlation with messages/contacts/prospects.
|
|
|
|
## Direction
|
|
|
|
Read-only. Call logs are append-only records emitted by the system; there is
|
|
no Sender or send queue.
|
|
|
|
## OS surface
|
|
|
|
`~/Library/Application Support/CallHistoryDB/CallHistory.storedata` (and legacy
|
|
`~/Library/CallHistoryDB/CallHistory.storedata`).
|
|
|
|
Direct readonly SQLite via GRDB (Core Data schema: `ZCALLRECORD`, `ZUNIQUE_ID`,
|
|
`ZDATE` as Apple reference time, `ZADDRESS`, `ZORIGINATED`, `ZANSWERED`,
|
|
`ZDURATION`, `ZCALLTYPE`, `ZSERVICE_PROVIDER`, `ZNAME`).
|
|
|
|
`com.apple.security.files.all` entitlement (already present for chat.db) is
|
|
sufficient. No extra TCC prompt beyond Full Disk Access.
|
|
|
|
## Files
|
|
|
|
- `Reader.swift` — `CallHistoryReader`:
|
|
- Tries current (`Application Support/CallHistoryDB`) then legacy path.
|
|
- `fetchCalls(since: Date?)` — filters on `ZDATE > ref`, oldest-first.
|
|
- **WAL snapshot for freshness**: `makeSnapshot` copies the live `.storedata`
|
|
+ `-wal` + `-shm` into a temp dir; `readCalls` opens the copy read-write so
|
|
SQLite replays un-checkpointed frames. Recent calls (critical for triage)
|
|
are no longer missed. Snapshot is cleaned up with `defer`.
|
|
- Timestamp conversion: `Date(timeIntervalSinceReferenceDate:)`.
|
|
- Basic CNContact enrichment for display names when `ZNAME` absent (best-effort).
|
|
- Uses `PhoneUtils.normalize`. Exposed via `isAccessible` for Base auth gating.
|
|
- `APIClient.swift`:
|
|
- `syncCalls(_:)` → `POST /client/icalls/sync`
|
|
- `getStats()` → `GET /client/icalls/stats`
|
|
- `SyncManager.swift` — 120s read interval (per operator request), no send queue
|
|
hooks. Batches of 200. Simple `syncedThisSession` + total from server.
|
|
Authorization hooks gate on `reader.isAccessible` (Full Disk).
|
|
|
|
## Timing
|
|
|
|
- Read interval: **120s** (`@packages/icalls/Sources/ICallsSync/SyncManager.swift`).
|
|
|
|
## Server
|
|
|
|
- Entity: `macsync.calls` (dedicated table, not mixed into messages).
|
|
Unique on `(device_id, external_id)`.
|
|
- Ingest: `features/icalls/ingestCalls` (dedup + upsert). Records `calls_synced`
|
|
in sync-history.
|
|
- Queries: `/my/calls` (filter by deviceId, direction, callType, since (ISO),
|
|
limit, offset) + `/my/calls/stats`. Returns address, normalizedAddress,
|
|
contactName, direction, callType (telephony/facetime_*), answered,
|
|
durationSeconds, startedAt, serviceProvider. Sorted newest-first for triage use.
|
|
- Client ingest: `/client/icalls/sync` + `/stats` (wrapped in sync-history 'calls').
|
|
- MCP: `recent_calls` tool (mcp/) exposes the surface for agents/triage.
|
|
|
|
## Local Mac access (no remote server hop)
|
|
|
|
- `LocalWebServer` (`/api/calls?limit=N`) — structured recent calls, same shape
|
|
as server. Useful for "Control your Mac" agents or local tools. Also reports
|
|
`callHistoryDb` (available + error) in `/api/diagnostics` (shown in Dashboard
|
|
"Native Subsystems" card).
|
|
- Same Full Disk grant as iMessage (no extra prompt).
|
|
|
|
## Web
|
|
|
|
- `web/src/api/calls.ts` + `fetchCalls`/`fetchCallStats`
|
|
- `web/src/tabs/Calls/index.tsx` (filter by direction/callType, table view)
|
|
- Integrated in `App.tsx` route, `AppShell` nav, Dashboard KPI tile (from
|
|
throughput) + "Call History DB" row in Native Subsystems (from diagnostics).
|
|
- Prospect cockpit `/thread` also surfaces `recentCalls` for the handle.
|
|
|
|
## Known characteristics
|
|
|
|
- First sync backfills available history (can be thousands of rows; rows are tiny).
|
|
- `ZUNIQUE_ID` (or synthetic `zpk:<Z_PK>`) is the stable external id.
|
|
- Multi-party FaceTime participants live in join tables (`Z_2REMOTEPARTICIPANTHANDLES` + `ZHANDLE`); v1 surfaces primary `ZADDRESS` only.
|
|
- Duration 0 + unanswered often indicates missed/ringing-out attempts.
|
|
- See [known limitations](./known-limitations.md#icalls-limitations-mac-callhistory-only)
|
|
for WAL snapshot details, iPhone cellular gap, etc.
|
|
|
|
## Adding / extending
|
|
|
|
Follow the read-only path in `docs/adding-a-module.md` (no send queue / Sender / admin
|
|
send routes). Add columns or participant join expansion in a follow-up migration if needed.
|