@ascenda-one/history-import 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -0
- package/dist/cli.js +3889 -0
- package/dist/cli.js.map +1 -0
- package/dist/daySlice.js +98 -0
- package/dist/daySlice.js.map +1 -0
- package/dist/extractors/claudeCode.js +779 -0
- package/dist/extractors/claudeCode.js.map +1 -0
- package/dist/extractors/cursor.js +601 -0
- package/dist/extractors/cursor.js.map +1 -0
- package/dist/extractors/git.js +9 -0
- package/dist/extractors/git.js.map +1 -0
- package/dist/extractors/vscode.js +615 -0
- package/dist/extractors/vscode.js.map +1 -0
- package/dist/localHandoff.js +238 -0
- package/dist/localHandoff.js.map +1 -0
- package/dist/retention.js +68 -0
- package/dist/retention.js.map +1 -0
- package/dist/scan.js +220 -0
- package/dist/scan.js.map +1 -0
- package/dist/ship.js +208 -0
- package/dist/ship.js.map +1 -0
- package/dist/staging.js +121 -0
- package/dist/staging.js.map +1 -0
- package/dist/stores.js +25 -0
- package/dist/stores.js.map +1 -0
- package/dist/types.js +74 -0
- package/dist/types.js.map +1 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# @ascenda-one/history-import
|
|
2
|
+
|
|
3
|
+
Retrospective AI-usage import: extracts the baseline that already exists on
|
|
4
|
+
an engineer's machine — Claude Code transcripts, Cursor's conversation store,
|
|
5
|
+
VS Code local history and Copilot sessions, git — and ships it as
|
|
6
|
+
provenance-classed historical events on the existing telemetry wire.
|
|
7
|
+
|
|
8
|
+
The research behind this package (what is actually in each store, verified on
|
|
9
|
+
a real machine 2026-08-18) lives in the Flow workspace:
|
|
10
|
+
`asc-ascenda-app-workspace/docs/HISTORICAL_TELEMETRY_IMPORT.md`. Read it
|
|
11
|
+
before touching an extractor — the store formats are reverse-engineered and
|
|
12
|
+
every design rule here traces to a finding there.
|
|
13
|
+
|
|
14
|
+
## Why this package exists here, not in the app
|
|
15
|
+
|
|
16
|
+
The macOS Flow app is sandboxed and child processes inherit the sandbox, so
|
|
17
|
+
it can never read `~/.claude` or the editors' Application Support stores. The
|
|
18
|
+
app is the consent and display surface; it hands the user one terminal
|
|
19
|
+
command (same pattern as hooks pairing) and this CLI does the reading.
|
|
20
|
+
|
|
21
|
+
## Design rules (non-negotiable)
|
|
22
|
+
|
|
23
|
+
1. **Evaporation order.** Claude Code first — its 30-day rolling purge is
|
|
24
|
+
deleting a day of baseline per day. Then Cursor, then VS Code, then git.
|
|
25
|
+
2. **Copy, then parse.** Extraction only ever reads a staged snapshot
|
|
26
|
+
(`src/staging.ts`), never live files. SQLite snapshots carry their `-wal`.
|
|
27
|
+
3. **Sniff per record, dispatch on the self-labelled version** (`version` on
|
|
28
|
+
Claude lines, `_v` on Cursor records, `version: 1` in VS Code entries).
|
|
29
|
+
Unknown shapes become `historical_unparsed` — raw retained in staging,
|
|
30
|
+
nothing inferred. Fixture tests per known (tool, version) pair.
|
|
31
|
+
4. **Metrics only by default.** Prompt/response text, thinking blocks and
|
|
32
|
+
file contents never leave the machine. Content-level ingestion, if it ever
|
|
33
|
+
ships, is a separate explicit opt-in — not this package's default path.
|
|
34
|
+
5. **Aggregate before shipping.** Per-session / per-day events, not one event
|
|
35
|
+
per bubble (one observed machine holds 17,727 bubbles).
|
|
36
|
+
6. **Provenance is data.** Every event carries `historical_direct`,
|
|
37
|
+
`historical_derived` or `historical_unparsed` — never the live
|
|
38
|
+
`ai_work_telemetry` provenance — so no chart can pass history off as
|
|
39
|
+
live observation.
|
|
40
|
+
|
|
41
|
+
## Status (2026-08-18)
|
|
42
|
+
|
|
43
|
+
| Piece | State |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `scan` (per-store inventory, content never opened) | implemented |
|
|
46
|
+
| `fix-retention` (Claude `cleanupPeriodDays`, merge-not-clobber) | implemented |
|
|
47
|
+
| Staging/snapshot (copy-then-parse, WAL-aware, APFS clone-on-write) | implemented |
|
|
48
|
+
| **Claude Code extractor** (human-prompt/tool-result split, session folds incl. recursive subagent transcripts, after-hours, compaction, tool failures, context-window peak, human-corrected edits, correction cadence, gap-split active minutes, epoch marker) | **implemented, verified live** |
|
|
49
|
+
| **Batch shipper** (`POST /v1/tool-events/batch`, salted hashes, stable importKey) | **implemented, verified live** |
|
|
50
|
+
| **Cursor extractor** (composerHeaders + bubble aggregation via SQL-side `json_extract`, prompt text never parsed into the process, subagent-composer folding, epoch marker) | **implemented, verified live** |
|
|
51
|
+
| **VS Code extractor** (Timeline-history Chat-Edit day×workspace aggregation, Copilot chatSessions folding, workspace identity via `workspace.json` longest-prefix match, epoch marker) | **implemented, verified live** |
|
|
52
|
+
| `import [--ship]` end to end | **dry run 2026-08-18: Claude Code 9,198 events (333 sessions, 8,401 human prompts, 135 after-hours sessions, 261 sessions with ≥1 tool failure) + Cursor 661 events (104 sessions, 550 human prompts, 6 after-hours sessions) + VS Code 7,338 events (548 edit-days summing to the full 13,780 verified Chat Edits — monthly rollup reproduces the documented May-2026 cliff: Apr 2,079 → May 343 → Jun 1,047 with no manual tuning; 419 Copilot sessions, 6,131 human prompts, 114 after-hours sessions, 125 sessions with ≥1 request error; 2 Timeline-history files unparsed) — 17,197 total across all three stores. Last `--ship` run: 8,720/8,720 accepted by the backend, 2026-08-18, before this extractor's friction-signal, Cursor and VS Code additions.** |
|
|
53
|
+
| git extractor | stub — throws with a pointer |
|
|
54
|
+
| zsh `EXTENDED_HISTORY` apply | snippet only |
|
|
55
|
+
|
|
56
|
+
The human-prompt classifier is the load-bearing piece: on the verified store,
|
|
57
|
+
108,528 user-role lines reduce to 8,272 actual typed prompts — the other 92%
|
|
58
|
+
are tool-result round-trips that would have inflated every prompt metric ~13x.
|
|
59
|
+
|
|
60
|
+
## Gaps that block a real user running this twice
|
|
61
|
+
|
|
62
|
+
Both blockers are **implemented but unmerged** — asc-core-be branch
|
|
63
|
+
`claude/historical-import-dedup-and-consent`, not `origin/main` (verified
|
|
64
|
+
19 Aug 2026). Neither is enforced by the backend a published client would
|
|
65
|
+
actually reach. What they become **once that branch merges**:
|
|
66
|
+
|
|
67
|
+
- **Consent scope — specified, not yet enforced.** `historical_import` is a
|
|
68
|
+
real `ToolConsentScope` here, and on that branch it is backed by
|
|
69
|
+
`ConsentType.HistoricalImport` (507) with ingestion enforcing it.
|
|
70
|
+
|
|
71
|
+
On `origin/main` today none of that exists: `ResolveConsentType` does not
|
|
72
|
+
recognise the scope and falls to its default arm, `AiDataProcessing` — the
|
|
73
|
+
lease already granted for live IDE telemetry. A backfill would ride in on
|
|
74
|
+
live-telemetry consent, which is exactly what the separate scope exists to
|
|
75
|
+
stop. **This gates publishing the package**, independently of any tier or
|
|
76
|
+
entitlement decision: paying for a capability is not the same act as
|
|
77
|
+
agreeing to a specific read of nine months of local history.
|
|
78
|
+
|
|
79
|
+
How it behaves once merged: Enforcement keys on the event's **provenance**, not
|
|
80
|
+
on the scope string it sends: any of the three `historical_*` classes
|
|
81
|
+
requires an active historical-import lease, so a client that keeps claiming
|
|
82
|
+
`ide_telemetry` over backdated events is rejected rather than waved through
|
|
83
|
+
to the default consent type. Nothing grants the lease at pairing — a paired
|
|
84
|
+
tool has consent to watch you from now on, never to read backwards.
|
|
85
|
+
- **Idempotency — closed.** `(pairedUser, toolInstallation, importKey)` is
|
|
86
|
+
unique in the database, checked before insert and enforced by a partial
|
|
87
|
+
unique index for the concurrent case. A replayed event answers `duplicate`,
|
|
88
|
+
writes nothing at all (not the event, not `lastSeenAt`, not an audit row),
|
|
89
|
+
and the batch response counts duplicates apart from both accepted and
|
|
90
|
+
rejected. `importKey` is **required** on any historical event. (Same caveat:
|
|
91
|
+
this is the branch's behaviour, not main's.)
|
|
92
|
+
|
|
93
|
+
**The dedup key is the source record ref alone — not `extractionId` + ref,
|
|
94
|
+
as this list originally said.** An extraction id is minted per run, so
|
|
95
|
+
including it would make every key unique and dedup nothing, which is exactly
|
|
96
|
+
the case a re-run is. The source record is the stable identity, so it is the
|
|
97
|
+
whole key; a re-run with a fresh `extractionId` over the same records
|
|
98
|
+
therefore still dedups, and the first run's extraction stays on record.
|
|
99
|
+
Pinned by `HistoricalImportIngestTests` in asc-core-be.
|
|
100
|
+
- **The key survives a changing store.** `ordinal` numbers an event only
|
|
101
|
+
among events sharing its whole identity — store, session, kind, instant —
|
|
102
|
+
so it separates genuine same-millisecond duplicates without encoding the
|
|
103
|
+
event's position in the run. Deleting a day, a session or an entire store
|
|
104
|
+
between runs leaves every surviving key untouched. Pinned by
|
|
105
|
+
`tests/importKeyStability.test.mjs`. (Until 20 Aug 2026 this was the
|
|
106
|
+
event's index in the whole shipped array, which re-keyed unchanged records
|
|
107
|
+
after a purge — the one case a re-run is for.)
|
|
108
|
+
|
|
109
|
+
Still open, and worth knowing before trusting a second run completely:
|
|
110
|
+
|
|
111
|
+
- **The 18 Aug 2026 shipment cannot be deduped against.** Production accepted
|
|
112
|
+
8,720 backdated events from one machine before the `ImportKey` column
|
|
113
|
+
existed, so those rows carry `NULL` and the unique index is filtered on
|
|
114
|
+
`IS NOT NULL`. Nothing will ever match them. A clean re-ship needs them
|
|
115
|
+
removed backend-side first, or that window lands twice — once. Runs from
|
|
116
|
+
here are idempotent with each other.
|
|
117
|
+
- **Epoch markers** (Copilot→Cursor→Claude eras, Ascenda install) are not yet
|
|
118
|
+
event types anywhere.
|
|
119
|
+
|
|
120
|
+
## Usage
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
ascenda-history-import scan # human-readable inventory
|
|
124
|
+
ascenda-history-import scan --json # what the app's consent surface renders
|
|
125
|
+
ascenda-history-import fix-retention # dry-run; --apply to write
|
|
126
|
+
ascenda-history-import import # Claude Code + Cursor + VS Code, dry run; --ship to send
|
|
127
|
+
```
|