@ascenda-one/history-import 0.1.14 → 0.1.16
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 +89 -8
- package/dist/activeSplit.js +159 -0
- package/dist/activeSplit.js.map +1 -0
- package/dist/cli.js +2273 -249
- package/dist/cli.js.map +1 -1
- package/dist/daySlice.js +22 -4
- package/dist/daySlice.js.map +1 -1
- package/dist/extractors/claudeCode.js +205 -23
- package/dist/extractors/claudeCode.js.map +1 -1
- package/dist/extractors/cursor.js +97 -4
- package/dist/extractors/cursor.js.map +1 -1
- package/dist/extractors/vscode.js +95 -2
- package/dist/extractors/vscode.js.map +1 -1
- package/dist/localHandoff.js +139 -21
- package/dist/localHandoff.js.map +1 -1
- package/dist/ship.js +90 -5
- package/dist/ship.js.map +1 -1
- package/dist/toolName.js +33 -0
- package/dist/toolName.js.map +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,8 +30,15 @@ command (same pattern as hooks pairing) and this CLI does the reading.
|
|
|
30
30
|
4. **Metrics only by default.** Prompt/response text, thinking blocks and
|
|
31
31
|
file contents never leave the machine. Content-level ingestion, if it ever
|
|
32
32
|
ships, is a separate explicit opt-in — not this package's default path.
|
|
33
|
-
5. **Aggregate before shipping
|
|
34
|
-
per bubble — a single machine's stores hold
|
|
33
|
+
5. **Aggregate before shipping, unless a reader counts the rows.** Per-session
|
|
34
|
+
/ per-day events, not one event per bubble — a single machine's stores hold
|
|
35
|
+
tens of thousands of them. The one deliberate exception is
|
|
36
|
+
`ai_tool_call_started`: the backend's work-demand rail derives
|
|
37
|
+
`toolCallCount` by counting rows of that type and reads no `toolCallCount`
|
|
38
|
+
key off metadata, so a session-level aggregate would ship, store, and be
|
|
39
|
+
counted by nothing. Per-call events also place the work in the right hour,
|
|
40
|
+
which a session spanning six of them cannot. Expect an order of magnitude
|
|
41
|
+
more events than a session-only import, and an `events.jsonl` to match.
|
|
35
42
|
6. **Provenance is data.** Every event carries `historical_direct`,
|
|
36
43
|
`historical_derived` or `historical_unparsed` — never the live
|
|
37
44
|
`ai_work_telemetry` provenance — so no chart can pass history off as
|
|
@@ -46,6 +53,8 @@ command (same pattern as hooks pairing) and this CLI does the reading.
|
|
|
46
53
|
| Staging/snapshot (copy-then-parse, WAL-aware, **torn down by the run that makes it**) | implemented |
|
|
47
54
|
| `archive` (durable content-addressed copy, dedup, verify, restore, prune) | **implemented, verified on a real 4.1 GB store** |
|
|
48
55
|
| **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** |
|
|
56
|
+
| **Active-time split** (hands-on vs agent-supervising, per session, per local day and per project digest; autonomy bands off the transcript's own `permissionMode`) | **implemented, verified against a real 400-session store** |
|
|
57
|
+
| **Tool-call counting, all three stores** (`tool_use` items / `toolFormerData` / `toolInvocationSerialized`, one `ai_tool_call_started` per call) | **implemented; exercised end to end against all three stores on a developer machine** |
|
|
49
58
|
| **Batch shipper** (`POST /v1/tool-events/batch`, salted hashes, stable importKey) | **implemented, verified live** |
|
|
50
59
|
| **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
60
|
| **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** |
|
|
@@ -58,12 +67,84 @@ user-role transcript lines are tool-result round-trips, not typed prompts.
|
|
|
58
67
|
Conflating the two inflates every prompt metric by roughly an order of
|
|
59
68
|
magnitude, so its fixtures are the ones to keep green.
|
|
60
69
|
|
|
61
|
-
##
|
|
70
|
+
## Active time is two figures, never one
|
|
62
71
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
`activeMinutes` answers "how much of this session was not idle": every
|
|
73
|
+
known-line timestamp, main thread and subagents merged, gap-split at five
|
|
74
|
+
minutes. It has always been the honest alternative to wall clock, and it is
|
|
75
|
+
unchanged.
|
|
76
|
+
|
|
77
|
+
It is not, on its own, an answer to "how long did this take me". One prompt can
|
|
78
|
+
drive a forty-minute agent run, and forty minutes of an agent working is not
|
|
79
|
+
forty minutes of a person at a keyboard. So the same material is also reported
|
|
80
|
+
split:
|
|
81
|
+
|
|
82
|
+
| Figure | What it is |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `handsOnMinutes` | The interval immediately **preceding** a human prompt. The prompt at its end is the evidence: someone read the previous output and typed. |
|
|
85
|
+
| `agentSupervisingMinutes` | Every other active interval. The agent produced the lines that bound it. |
|
|
86
|
+
|
|
87
|
+
The two partition `activeMinutes` exactly and there is **no third key holding
|
|
88
|
+
their sum**, at session, day or project scale. Adding them reconstructs
|
|
89
|
+
`activeMinutes`, which already exists; a differently-named total would be the
|
|
90
|
+
same number wearing a claim it cannot support. On a real 400-session store the
|
|
91
|
+
split came out 1,074 hands-on minutes against 36,948 supervising — quoting the
|
|
92
|
+
combined 38,035 as time spent is off by a factor of thirty-five for the half a
|
|
93
|
+
person would recognise as their own.
|
|
94
|
+
|
|
95
|
+
**`agentSupervisingMinutes` does not claim anyone was watching**, and nothing
|
|
96
|
+
in a transcript could show that they were. It is time the agent was working
|
|
97
|
+
which the person did not spend typing. Rendering it as attention is a
|
|
98
|
+
fabrication the name invites and the data does not support; the honest gloss is
|
|
99
|
+
"the agent was working".
|
|
100
|
+
|
|
101
|
+
### Autonomy bands
|
|
102
|
+
|
|
103
|
+
`permissionMode` is on the transcript's human-prompt lines and nowhere else —
|
|
104
|
+
across 120 real stores it appears on 6.7% of `user` lines and on no
|
|
105
|
+
`assistant`, `system` or `attachment` line. So posture is known at prompt
|
|
106
|
+
boundaries and carried forward between them, and supervising minutes are banded
|
|
107
|
+
by it through `autonomyBand`. Time before the first declaration lands in
|
|
108
|
+
`unknown` and is never folded into a neighbouring band.
|
|
109
|
+
|
|
110
|
+
The band map rides in the local handoff only (`autonomySplit`), never on the
|
|
111
|
+
wire: banding is a reader's vocabulary derived from the stored token at query
|
|
112
|
+
time, and storing the band would freeze a decision deliberately left open.
|
|
113
|
+
|
|
114
|
+
### The counters
|
|
115
|
+
|
|
116
|
+
Three diagnostics ship with the split, read by neither the backend nor the
|
|
117
|
+
handoff on purpose:
|
|
118
|
+
|
|
119
|
+
- `activeSplitInstants` — distinct timestamps the split ran over. Two minutes
|
|
120
|
+
off four instants and off four hundred are not the same measurement.
|
|
121
|
+
- `activeSplitUndatedLines` — known lines whose `timestamp` would not parse.
|
|
122
|
+
They still move the session's wall clock by string comparison, so only this
|
|
123
|
+
says both active figures are short.
|
|
124
|
+
- `activeSplitUnposturedInstants` — instants reached before any
|
|
125
|
+
`permissionMode` was declared. The posture blind spot as a count, not
|
|
126
|
+
inferred from the `unknown` band being present.
|
|
127
|
+
|
|
128
|
+
### The defect this replaced
|
|
129
|
+
|
|
130
|
+
The per-day slices used to gap-split the **prompt timestamps** while the
|
|
131
|
+
session figure gap-split the **whole timeline**. The threshold was shared and
|
|
132
|
+
commented as keeping one definition of "active"; the material was not. Across
|
|
133
|
+
200 real sessions the prompts-only reading came to 2,730 minutes against
|
|
134
|
+
18,938 — an 85.6% under-report, concentrated exactly on the sessions where an
|
|
135
|
+
agent did the most work. Both now cross the call, and
|
|
136
|
+
`tests/activeSplit.test.mjs` pins it against a real transcript.
|
|
137
|
+
|
|
138
|
+
## What the backend enforces on a second run
|
|
139
|
+
|
|
140
|
+
Both of the blockers this section used to list — the consent scope and
|
|
141
|
+
idempotency — are enforced by the deployed backend, and have been since
|
|
142
|
+
20 Aug 2026 (verified against a real backfill on 25 Aug 2026). They stopped
|
|
143
|
+
gating publication then, and the package ships from the release tag like
|
|
144
|
+
every other CLI here. Two things stand apart from them and are decided
|
|
145
|
+
elsewhere: which tier the batch ship belongs to, and the consent surface that
|
|
146
|
+
grants the lease. Without that grant every event of a backfill is rejected
|
|
147
|
+
`consent_missing_or_expired` — the gate working, not a broken token.
|
|
67
148
|
|
|
68
149
|
- **Consent scope.** `historical_import` is a distinct `ToolConsentScope`,
|
|
69
150
|
separate from the lease granted for live IDE telemetry, and a backfill
|
|
@@ -119,7 +200,7 @@ ascenda-history-import archive # the durable copy; --verify / --list / -
|
|
|
119
200
|
### Staging is scaffolding; `archive` is the copy
|
|
120
201
|
|
|
121
202
|
`import` snapshots each store, extracts, and **deletes the snapshot in a
|
|
122
|
-
`finally`** — success or failure — keeping only
|
|
203
|
+
`finally`** — success or failure — keeping only `events.jsonl`.
|
|
123
204
|
It also sweeps snapshots left by earlier runs. This is not tidiness: nineteen
|
|
124
205
|
runs once left 254 GB on a 926 GB disk and took free space to 279 MB, and the
|
|
125
206
|
first thing to notice was unrelated tooling failing with `ENOSPC`.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Active time, split by who was doing the work.
|
|
3
|
+
*
|
|
4
|
+
* `activeMinutes` already answers "how much of this session was not idle" by
|
|
5
|
+
* gap-splitting every known-line timestamp. It cannot answer the question the
|
|
6
|
+
* Reveal actually wants to ask, which is what that time *was*: one prompt can
|
|
7
|
+
* drive a forty-minute agent run, and forty minutes of watching an agent is
|
|
8
|
+
* not forty minutes of a person at a keyboard. A single figure reports them as
|
|
9
|
+
* the same thing.
|
|
10
|
+
*
|
|
11
|
+
* This module splits the same gap-split material in two:
|
|
12
|
+
*
|
|
13
|
+
* - **hands-on** — the interval immediately *preceding* a human prompt. The
|
|
14
|
+
* prompt at its end is the evidence: someone read the previous output and
|
|
15
|
+
* typed. It is the only interval in a transcript where a person is
|
|
16
|
+
* demonstrably present, because it is the only one a person signed.
|
|
17
|
+
* - **agent-supervising** — every other active interval. The agent produced
|
|
18
|
+
* the lines that bound it.
|
|
19
|
+
*
|
|
20
|
+
* **What "supervising" does not claim.** It does not claim the person was
|
|
21
|
+
* watching, and nothing in a transcript could show that they were. It is time
|
|
22
|
+
* the agent was working which the person did not spend typing — no more. A
|
|
23
|
+
* reader that renders it as attention is reading a fabrication into it; the
|
|
24
|
+
* name is the plan's, the bound is this paragraph. The honest gloss is
|
|
25
|
+
* "the agent was working", not "you were supervising".
|
|
26
|
+
*
|
|
27
|
+
* **Two figures, never one total.** Nothing here returns their sum. They
|
|
28
|
+
* partition the same active milliseconds exactly (pinned by test), so a caller
|
|
29
|
+
* *can* add them — but adding them reconstructs `activeMinutes`, which already
|
|
30
|
+
* exists and already has readers. A third number spelled like a new
|
|
31
|
+
* measurement would be the same number wearing a claim it cannot support, and
|
|
32
|
+
* the whole point of the split is that the two halves differ.
|
|
33
|
+
*
|
|
34
|
+
* ## Posture
|
|
35
|
+
*
|
|
36
|
+
* `permissionMode` is on the transcript's human prompt lines and nowhere else
|
|
37
|
+
* — checked across 120 real stores: 6.7% of `user` lines carry it, no
|
|
38
|
+
* `assistant`, `system` or `attachment` line ever does. So posture is known at
|
|
39
|
+
* prompt boundaries and interpolated between them: the mode declared by the
|
|
40
|
+
* most recent human prompt at or before an interval governs that interval,
|
|
41
|
+
* because that is when the person last told the runtime what it could do
|
|
42
|
+
* unasked.
|
|
43
|
+
*
|
|
44
|
+
* Before the first declaration there is nothing to carry forward, and those
|
|
45
|
+
* milliseconds land in `unknown` — never folded into a neighbouring band, for
|
|
46
|
+
* the reason `autonomyBand` states at length: a guess there would look exactly
|
|
47
|
+
* like a measurement. `unknown` being non-zero is normal and is not a defect.
|
|
48
|
+
*
|
|
49
|
+
* Only supervising time is banded. Posture describes how much latitude the
|
|
50
|
+
* agent had while working; applied to the interval where the person was typing
|
|
51
|
+
* it would describe nothing.
|
|
52
|
+
*/
|
|
53
|
+
import { autonomyBand } from "@ascenda-one/tool-kit";
|
|
54
|
+
/**
|
|
55
|
+
* Upstream's `permissionMode` spellings, snake-cased — the same one
|
|
56
|
+
* transformation `AutonomyMode` documents, applied here because the transcript
|
|
57
|
+
* writes camelCase where the hook payloads write snake_case. Mirroring rather
|
|
58
|
+
* than translating: a mode this table has never seen is passed through
|
|
59
|
+
* verbatim, so it reaches `autonomyBand` as an unrecognised token and lands in
|
|
60
|
+
* `unknown` instead of being quietly mapped onto whichever rung looks close.
|
|
61
|
+
*/
|
|
62
|
+
export function snakeCasePermissionMode(mode) {
|
|
63
|
+
return mode.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The active spans of a timeline, in order.
|
|
67
|
+
*
|
|
68
|
+
* **Ties are collapsed, not ordered.** A parallel tool batch writes several
|
|
69
|
+
* lines on one millisecond, and a human prompt can share an instant with the
|
|
70
|
+
* attachment lines that accompany it. Sorting such lines against each other
|
|
71
|
+
* would make the split depend on a within-millisecond order the store does not
|
|
72
|
+
* promise and the reader cannot see. So every line at one epoch millisecond
|
|
73
|
+
* becomes one instant, `human` if *any* of them was a human prompt and
|
|
74
|
+
* carrying whichever posture was declared there. Intervals between tied lines
|
|
75
|
+
* are zero-length and are not spans, so nothing is lost by it.
|
|
76
|
+
*/
|
|
77
|
+
export function activeSpans(points, options) {
|
|
78
|
+
const collapsed = new Map();
|
|
79
|
+
let undatedPoints = 0;
|
|
80
|
+
for (const point of points) {
|
|
81
|
+
if (!Number.isFinite(point.at)) {
|
|
82
|
+
undatedPoints += 1;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const existing = collapsed.get(point.at);
|
|
86
|
+
if (existing) {
|
|
87
|
+
existing.human = existing.human || point.human;
|
|
88
|
+
if (point.autonomyMode)
|
|
89
|
+
existing.autonomyMode = point.autonomyMode;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
collapsed.set(point.at, { human: point.human, autonomyMode: point.autonomyMode });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const instants = [...collapsed.keys()].sort((a, b) => a - b);
|
|
96
|
+
const report = {
|
|
97
|
+
spans: [],
|
|
98
|
+
undatedPoints,
|
|
99
|
+
instants: instants.length,
|
|
100
|
+
unposturedInstants: 0
|
|
101
|
+
};
|
|
102
|
+
if (instants.length === 0)
|
|
103
|
+
return report;
|
|
104
|
+
// The posture in force, carried forward from the last human prompt that
|
|
105
|
+
// declared one. Seeded from the first instant before the loop so a session
|
|
106
|
+
// whose opening prompt declares a mode is not credited with an unknown
|
|
107
|
+
// stretch it never had.
|
|
108
|
+
let posture = collapsed.get(instants[0])?.autonomyMode ?? null;
|
|
109
|
+
if (posture === null)
|
|
110
|
+
report.unposturedInstants += 1;
|
|
111
|
+
for (let i = 1; i < instants.length; i += 1) {
|
|
112
|
+
const from = instants[i - 1];
|
|
113
|
+
const to = instants[i];
|
|
114
|
+
const here = collapsed.get(to);
|
|
115
|
+
const gap = to - from;
|
|
116
|
+
if (gap > 0 && gap <= options.activeGapMs) {
|
|
117
|
+
// `autonomyBand` maps a null or unrecognised token to `unknown` on its
|
|
118
|
+
// own; passing the raw carried value keeps that decision in the one
|
|
119
|
+
// place that documents it.
|
|
120
|
+
report.spans.push({ from, to, handsOn: here.human, band: autonomyBand(posture) });
|
|
121
|
+
}
|
|
122
|
+
if (here.autonomyMode)
|
|
123
|
+
posture = here.autonomyMode;
|
|
124
|
+
else if (posture === null)
|
|
125
|
+
report.unposturedInstants += 1;
|
|
126
|
+
}
|
|
127
|
+
return report;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Splits a session's timeline into hands-on and agent-supervising time, by
|
|
131
|
+
* summing {@link activeSpans}.
|
|
132
|
+
*/
|
|
133
|
+
export function splitActiveTime(points, options) {
|
|
134
|
+
const report = activeSpans(points, options);
|
|
135
|
+
const split = {
|
|
136
|
+
handsOnMs: 0,
|
|
137
|
+
agentSupervisingMs: 0,
|
|
138
|
+
supervisingMsByBand: {},
|
|
139
|
+
undatedPoints: report.undatedPoints,
|
|
140
|
+
instants: report.instants,
|
|
141
|
+
unposturedInstants: report.unposturedInstants
|
|
142
|
+
};
|
|
143
|
+
for (const span of report.spans) {
|
|
144
|
+
const ms = span.to - span.from;
|
|
145
|
+
if (span.handsOn) {
|
|
146
|
+
split.handsOnMs += ms;
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
split.agentSupervisingMs += ms;
|
|
150
|
+
split.supervisingMsByBand[span.band] = (split.supervisingMsByBand[span.band] ?? 0) + ms;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return split;
|
|
154
|
+
}
|
|
155
|
+
/** Whole minutes, rounded once at the edge. */
|
|
156
|
+
export function minutesOf(ms) {
|
|
157
|
+
return Math.round(ms / 60_000);
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=activeSplit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activeSplit.js","sourceRoot":"","sources":["../src/activeSplit.ts"],"names":[],"mappings":"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"}
|