@teeras/afterhours 0.1.1 → 0.3.1
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 +101 -15
- package/dist/activity.d.ts +35 -1
- package/dist/activity.d.ts.map +1 -1
- package/dist/activity.js +148 -1
- package/dist/activity.js.map +1 -1
- package/dist/autosync.d.ts +22 -0
- package/dist/autosync.d.ts.map +1 -0
- package/dist/autosync.js +85 -0
- package/dist/autosync.js.map +1 -0
- package/dist/cli.js +112 -9
- package/dist/cli.js.map +1 -1
- package/dist/cloud.d.ts +5 -0
- package/dist/cloud.d.ts.map +1 -1
- package/dist/cloud.js +10 -2
- package/dist/cloud.js.map +1 -1
- package/dist/collect.d.ts +3 -1
- package/dist/collect.d.ts.map +1 -1
- package/dist/collect.js +19 -7
- package/dist/collect.js.map +1 -1
- package/dist/detect.d.ts.map +1 -1
- package/dist/detect.js +35 -6
- package/dist/detect.js.map +1 -1
- package/dist/hooks.d.ts +29 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +196 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts +25 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +155 -0
- package/dist/install.js.map +1 -0
- package/dist/moments.d.ts +21 -0
- package/dist/moments.d.ts.map +1 -0
- package/dist/moments.js +52 -0
- package/dist/moments.js.map +1 -0
- package/dist/off.d.ts +24 -0
- package/dist/off.d.ts.map +1 -0
- package/dist/off.js +36 -0
- package/dist/off.js.map +1 -0
- package/dist/privacy.d.ts +8 -0
- package/dist/privacy.d.ts.map +1 -1
- package/dist/privacy.js +27 -1
- package/dist/privacy.js.map +1 -1
- package/dist/qr.d.ts +29 -0
- package/dist/qr.d.ts.map +1 -0
- package/dist/qr.js +92 -0
- package/dist/qr.js.map +1 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +61 -1
- package/dist/render.js.map +1 -1
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +115 -1
- package/dist/report.js.map +1 -1
- package/dist/runtime.d.ts +18 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +70 -0
- package/dist/runtime.js.map +1 -0
- package/dist/secret.d.ts +9 -0
- package/dist/secret.d.ts.map +1 -0
- package/dist/secret.js +32 -0
- package/dist/secret.js.map +1 -0
- package/dist/setup.d.ts +18 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +155 -0
- package/dist/setup.js.map +1 -0
- package/dist/sources/claude.d.ts.map +1 -1
- package/dist/sources/claude.js +162 -18
- package/dist/sources/claude.js.map +1 -1
- package/dist/sources/codex.d.ts.map +1 -1
- package/dist/sources/codex.js +240 -21
- package/dist/sources/codex.js.map +1 -1
- package/dist/sources/cursor.d.ts.map +1 -1
- package/dist/sources/cursor.js +1 -0
- package/dist/sources/cursor.js.map +1 -1
- package/dist/sources/gemini.d.ts.map +1 -1
- package/dist/sources/gemini.js +85 -8
- package/dist/sources/gemini.js.map +1 -1
- package/dist/types.d.ts +109 -6
- package/dist/types.d.ts.map +1 -1
- package/llms-install.md +29 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -3,13 +3,20 @@
|
|
|
3
3
|
Private AI-life balance and usage analytics for Codex, Claude Code, Gemini CLI,
|
|
4
4
|
Cursor, and other coding agents.
|
|
5
5
|
|
|
6
|
+
## For AI agents
|
|
7
|
+
|
|
8
|
+
Installing this for your user? Run `npx @teeras/afterhours setup`, relay the
|
|
9
|
+
printed code and link to your user, and let the command finish. Full
|
|
10
|
+
instructions: [`llms-install.md`](./llms-install.md). Counts only — prompts,
|
|
11
|
+
code, output, repo names, and paths never leave the machine.
|
|
12
|
+
|
|
6
13
|
```sh
|
|
7
14
|
npx @teeras/afterhours
|
|
8
15
|
```
|
|
9
16
|
|
|
10
|
-
The first run
|
|
11
|
-
|
|
12
|
-
once and the short command is simply:
|
|
17
|
+
The first run reads supported agent activity locally and prints a seven-day
|
|
18
|
+
view of agent activity, human attention, and after-hours check-ins without an
|
|
19
|
+
account. Install it once and the short command is simply:
|
|
13
20
|
|
|
14
21
|
```sh
|
|
15
22
|
trs
|
|
@@ -19,19 +26,33 @@ trs login
|
|
|
19
26
|
trs sync
|
|
20
27
|
trs sources
|
|
21
28
|
trs privacy
|
|
29
|
+
trs hook-config codex
|
|
30
|
+
trs hook-config claude
|
|
22
31
|
```
|
|
23
32
|
|
|
24
33
|
Afterhours parses the local session histories already written by supported
|
|
25
34
|
coding agents and immediately reduces them to counts and hourly buckets. It
|
|
26
35
|
never retains or returns prompt text, source code, model output, repository
|
|
27
36
|
names, working directories, or file paths. Only the documented aggregate
|
|
28
|
-
snapshot syncs after
|
|
37
|
+
snapshot syncs after the user connects Teeras.
|
|
29
38
|
|
|
30
|
-
|
|
39
|
+
Support matrix:
|
|
31
40
|
|
|
32
|
-
- Codex app and CLI
|
|
33
|
-
|
|
34
|
-
|
|
41
|
+
- Codex desktop app and Codex CLI: activity, token, model, and episode
|
|
42
|
+
measurement from the shared `~/.codex/sessions` history. A distinct
|
|
43
|
+
`CODEX_HOME` is collected alongside the default App home.
|
|
44
|
+
- Claude Code CLI: activity, token, model, and episode measurement from
|
|
45
|
+
`~/.claude/projects`, plus a distinct `CLAUDE_CONFIG_DIR` when configured.
|
|
46
|
+
Subagent transcripts are excluded from primary episode counts rather than
|
|
47
|
+
double-counted as human delegations. Claude Code Desktop keeps separate
|
|
48
|
+
history, so its shared opt-in lifecycle hook is the trustworthy live path.
|
|
49
|
+
- Gemini CLI: activity, token, model, and episode measurement from current
|
|
50
|
+
`~/.gemini/tmp/*/chats` histories; older `logs.json` builds retain honest
|
|
51
|
+
session-level fidelity.
|
|
52
|
+
- Claude desktop: detected separately. General Claude chat and Cowork activity
|
|
53
|
+
are not claimed as measured because those surfaces do not expose a stable,
|
|
54
|
+
content-free local lifecycle feed. Claude Code remains a separate first-class
|
|
55
|
+
source.
|
|
35
56
|
- Cursor detection, ready for a consented adapter once its local schema is stable
|
|
36
57
|
|
|
37
58
|
`trs connect` also detects Windsurf, VS Code + Copilot, Zed, Amp, OpenCode,
|
|
@@ -40,18 +61,83 @@ the rest are reported honestly as requiring a source adapter. The exported
|
|
|
40
61
|
`SourceAdapter` contract lets any coding agent add counts-only support without
|
|
41
62
|
changing the Afterhours core.
|
|
42
63
|
|
|
43
|
-
## Keep
|
|
64
|
+
## Keep measuring across web and mobile
|
|
44
65
|
|
|
45
|
-
The first
|
|
46
|
-
Continue with Google, approve the counts-only boundary,
|
|
47
|
-
revocable
|
|
66
|
+
The first report stays on the computer. Run `trs connect` when you want to keep
|
|
67
|
+
your history in Teeras. Continue with Google, approve the counts-only boundary,
|
|
68
|
+
and the CLI receives a revocable 30-day device token. Google OAuth tokens never
|
|
69
|
+
enter the npm package.
|
|
48
70
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
71
|
+
Connecting syncs the first report immediately. After that, a counts-only
|
|
72
|
+
snapshot syncs as a side effect of your own agent activity: a lifecycle hook
|
|
73
|
+
event can trigger one detached, rate-limited sync (at most one every 45
|
|
74
|
+
minutes), so web and mobile stay current while you work. Every deliberate `trs`
|
|
75
|
+
run also refreshes the history. Use `trs sync` to sync without printing the
|
|
76
|
+
report, or `trs logout` to remove this computer's token. Nothing runs as a
|
|
77
|
+
background daemon, the hook itself makes no network call, and there is no
|
|
78
|
+
automatic LLM call.
|
|
52
79
|
|
|
53
80
|
The default command (and `trs week`) prints a calm seven-day report. Use
|
|
54
81
|
`--json` to inspect the privacy-safe snapshot consumed by the dashboard and
|
|
55
82
|
optional MCP adapter. The cloud keeps one replaceable snapshot per device per
|
|
56
83
|
day in a 790-day active-history window; the dashboard lets users export or
|
|
57
84
|
delete that history.
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
## Optional Codex and Claude Code lifecycle hooks
|
|
88
|
+
|
|
89
|
+
Afterhours can measure episode boundaries and permission requests more
|
|
90
|
+
accurately from opt-in lifecycle hooks.
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
# install the fragments for the agents found on this computer
|
|
94
|
+
npx @teeras/afterhours setup
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Running `setup` is the consent: it merges the Teeras fragment into the agents
|
|
98
|
+
it finds on this computer, leaves every other hook untouched, and is safe to
|
|
99
|
+
run again — a second run reports what is already connected instead of
|
|
100
|
+
duplicating it. The hook command it writes is an absolute path inside
|
|
101
|
+
`~/.teeras/afterhours`, so it keeps working after the `npx` directory is gone.
|
|
102
|
+
Remove the Teeras entries from the settings file to stop hook measurement, or
|
|
103
|
+
run `trs off` to do that for you — it clears the Teeras entries from both
|
|
104
|
+
agents' settings and deletes the local runtime, leaving every other hook and
|
|
105
|
+
your local journal and credentials untouched. If a settings file cannot be
|
|
106
|
+
read, `trs off` leaves both that file and the runtime alone and says so: the
|
|
107
|
+
entry it could not remove still points at the runtime, and deleting it would
|
|
108
|
+
leave your agent calling a command that is gone.
|
|
109
|
+
|
|
110
|
+
To review the configuration and merge it by hand instead:
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
# Codex CLI and Codex desktop app
|
|
114
|
+
trs hook-config codex
|
|
115
|
+
|
|
116
|
+
# Claude Code
|
|
117
|
+
trs hook-config claude
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
For Codex, review the fragment and merge it into
|
|
121
|
+
`$CODEX_HOME/hooks.json` (normally `~/.codex/hooks.json`), then review and trust
|
|
122
|
+
the hook in Codex. The user-level Codex configuration is shared by the desktop
|
|
123
|
+
app and CLI. For Claude Code, merge the fragment into
|
|
124
|
+
`$CLAUDE_CONFIG_DIR/settings.json` (normally `~/.claude/settings.json`).
|
|
125
|
+
Claude Code Desktop and CLI share hooks even though they maintain separate
|
|
126
|
+
session histories.
|
|
127
|
+
|
|
128
|
+
The hook command reduces each event immediately to a timestamp, an opaque
|
|
129
|
+
device-keyed session id, and one allowlisted lifecycle kind. The id is keyed
|
|
130
|
+
with a stable per-device secret (never a rotating login token), so episodes
|
|
131
|
+
stay consistent across logins. Prompt text, working directories, transcript
|
|
132
|
+
paths, tool inputs, notification messages, and outputs are discarded before
|
|
133
|
+
the local journal is written.
|
|
134
|
+
|
|
135
|
+
`trs sources` reports Codex and Claude Code hook status separately — event
|
|
136
|
+
count and last event time — so a misconfigured or logged-out hook is visible
|
|
137
|
+
instead of silently recording nothing. Passive local-history measurement
|
|
138
|
+
continues even if hooks are unavailable in an older client version.
|
|
139
|
+
|
|
140
|
+
Hooks improve measurement only. They do not currently trigger Walk Mode or
|
|
141
|
+
claim that an autonomous runway prediction is valid. Teeras first asks
|
|
142
|
+
participants to confirm episode boundaries and attention estimates in order to
|
|
143
|
+
validate the measurement contract.
|
package/dist/activity.d.ts
CHANGED
|
@@ -1,13 +1,47 @@
|
|
|
1
|
-
import type { ActivityBucket, SourceId, SourceSnapshot, TokenCounts } from "./types.js";
|
|
1
|
+
import type { ActivityBucket, DelegationEpisode, EpisodeConfidence, EpisodeLatency, LatencyStats, ModelActivity, SourceId, SourceSnapshot, TokenCounts } from "./types.js";
|
|
2
2
|
export interface LocalEvent {
|
|
3
3
|
at: number;
|
|
4
4
|
kind: "agent" | "human";
|
|
5
|
+
/** Model that produced an agent event; absent when the source names none. */
|
|
6
|
+
model?: string;
|
|
5
7
|
}
|
|
6
8
|
export declare const EMPTY_TOKENS: TokenCounts;
|
|
7
9
|
export declare function finiteNumber(value: unknown): number;
|
|
10
|
+
export declare function optionalFiniteNumber(value: unknown): number | null;
|
|
11
|
+
export declare function addToken(total: number | null, value: number | null): number | null;
|
|
8
12
|
export declare function timestamp(value: unknown): number | null;
|
|
9
13
|
export declare function inWindow(at: number, sinceMs: number, untilMs: number): boolean;
|
|
14
|
+
/** Local calendar date in the same format buckets and episodes use. */
|
|
15
|
+
export declare function localDateOf(at: number): string;
|
|
16
|
+
export declare const NO_LATENCY: EpisodeLatency;
|
|
17
|
+
export declare function latencyStats(samples: number[]): LatencyStats | null;
|
|
18
|
+
/**
|
|
19
|
+
* Gap from each human message to the first agent activity that follows it.
|
|
20
|
+
* A second human message before any agent activity restarts the clock — the
|
|
21
|
+
* person re-prompted, so the first wait was abandoned, not answered.
|
|
22
|
+
*/
|
|
23
|
+
export declare function humanWaitSamples(events: LocalEvent[]): number[];
|
|
24
|
+
export declare function opaqueEpisodeId(secret: string, sourceId: string, sourceSession: string): string;
|
|
10
25
|
export declare function bucketsFrom(events: LocalEvent[]): ActivityBucket[];
|
|
26
|
+
/**
|
|
27
|
+
* Per-model rollup of a source's work. Minutes use the same gap-to-next rule
|
|
28
|
+
* as the activity buckets, computed over ALL agent events so a model's share
|
|
29
|
+
* is its slice of the real timeline — a per-model subset would misread the
|
|
30
|
+
* gaps between models as one model's idle time. Events that never named a
|
|
31
|
+
* model shape the gaps but credit no entry, so the rollup can honestly sum
|
|
32
|
+
* to less than the source total.
|
|
33
|
+
*/
|
|
34
|
+
export declare function modelActivityFrom(activityEvents: LocalEvent[], turnEvents: LocalEvent[]): ModelActivity[];
|
|
11
35
|
export declare function emptySource(id: SourceId, name: string, configured: boolean): SourceSnapshot;
|
|
36
|
+
export declare function delegationEpisode(input: {
|
|
37
|
+
id: string;
|
|
38
|
+
adapterVersion: string;
|
|
39
|
+
events: LocalEvent[];
|
|
40
|
+
tokens: TokenCounts;
|
|
41
|
+
untilMs: number;
|
|
42
|
+
confidence?: EpisodeConfidence;
|
|
43
|
+
modelFamily?: string | null;
|
|
44
|
+
latency?: EpisodeLatency;
|
|
45
|
+
}): DelegationEpisode | null;
|
|
12
46
|
export declare function finishSource(source: SourceSnapshot, events: LocalEvent[], fidelity?: SourceSnapshot["fidelity"]): SourceSnapshot;
|
|
13
47
|
//# sourceMappingURL=activity.d.ts.map
|
package/dist/activity.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,QAAQ,EACR,cAAc,EACd,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,YAAY,EAAE,WAA0E,CAAC;AAEtG,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEnD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAElE;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAElF;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAIvD;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAE9E;AAED,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE9C;AAUD,eAAO,MAAM,UAAU,EAAE,cAAgF,CAAC;AAK1G,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,YAAY,GAAG,IAAI,CAKnE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,EAAE,CAe/D;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAQ/F;AAgBD,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,cAAc,EAAE,CAyBlE;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,cAAc,EAAE,UAAU,EAAE,EAC5B,UAAU,EAAE,UAAU,EAAE,GACvB,aAAa,EAAE,CA2BjB;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,cAAc,CAc3F;AA2BD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B,GAAG,iBAAiB,GAAG,IAAI,CA+B3B;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,UAAU,EAAE,EACpB,QAAQ,GAAE,cAAc,CAAC,UAAU,CAAc,GAChD,cAAc,CAMhB"}
|
package/dist/activity.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
import { createHmac } from "node:crypto";
|
|
2
|
+
export const EMPTY_TOKENS = { input: null, cached: null, output: null, reasoning: null };
|
|
2
3
|
export function finiteNumber(value) {
|
|
3
4
|
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
4
5
|
}
|
|
6
|
+
export function optionalFiniteNumber(value) {
|
|
7
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : null;
|
|
8
|
+
}
|
|
9
|
+
export function addToken(total, value) {
|
|
10
|
+
return value == null ? total : (total ?? 0) + value;
|
|
11
|
+
}
|
|
5
12
|
export function timestamp(value) {
|
|
6
13
|
if (typeof value !== "string" && typeof value !== "number")
|
|
7
14
|
return null;
|
|
@@ -11,6 +18,10 @@ export function timestamp(value) {
|
|
|
11
18
|
export function inWindow(at, sinceMs, untilMs) {
|
|
12
19
|
return at >= sinceMs && at <= untilMs;
|
|
13
20
|
}
|
|
21
|
+
/** Local calendar date in the same format buckets and episodes use. */
|
|
22
|
+
export function localDateOf(at) {
|
|
23
|
+
return localClock(at).localDate;
|
|
24
|
+
}
|
|
14
25
|
function localClock(at) {
|
|
15
26
|
const d = new Date(at);
|
|
16
27
|
const localDate = [d.getFullYear(), String(d.getMonth() + 1).padStart(2, "0"), String(d.getDate()).padStart(2, "0")].join("-");
|
|
@@ -18,6 +29,48 @@ function localClock(at) {
|
|
|
18
29
|
d.setMinutes(0, 0, 0);
|
|
19
30
|
return { hour: d.toISOString(), localDate, localHour: hourOfDay };
|
|
20
31
|
}
|
|
32
|
+
export const NO_LATENCY = { timeToFirstToken: null, turnDuration: null, humanWait: null };
|
|
33
|
+
/** Waits longer than this read as the person walking away, not the model. */
|
|
34
|
+
const MAX_HUMAN_WAIT_MS = 30 * 60_000;
|
|
35
|
+
export function latencyStats(samples) {
|
|
36
|
+
const clean = samples.filter((value) => Number.isFinite(value) && value >= 0).sort((a, b) => a - b);
|
|
37
|
+
if (clean.length === 0)
|
|
38
|
+
return null;
|
|
39
|
+
const rank = (q) => clean[Math.min(clean.length - 1, Math.ceil(q * clean.length) - 1)];
|
|
40
|
+
return { samples: clean.length, p50Ms: Math.round(rank(0.5)), p90Ms: Math.round(rank(0.9)) };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Gap from each human message to the first agent activity that follows it.
|
|
44
|
+
* A second human message before any agent activity restarts the clock — the
|
|
45
|
+
* person re-prompted, so the first wait was abandoned, not answered.
|
|
46
|
+
*/
|
|
47
|
+
export function humanWaitSamples(events) {
|
|
48
|
+
const sorted = [...events].sort((a, b) => a.at - b.at);
|
|
49
|
+
const waits = [];
|
|
50
|
+
let pendingHumanAt = null;
|
|
51
|
+
for (const event of sorted) {
|
|
52
|
+
if (event.kind === "human") {
|
|
53
|
+
pendingHumanAt = event.at;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (pendingHumanAt == null)
|
|
57
|
+
continue;
|
|
58
|
+
const wait = event.at - pendingHumanAt;
|
|
59
|
+
if (wait >= 0 && wait <= MAX_HUMAN_WAIT_MS)
|
|
60
|
+
waits.push(wait);
|
|
61
|
+
pendingHumanAt = null;
|
|
62
|
+
}
|
|
63
|
+
return waits;
|
|
64
|
+
}
|
|
65
|
+
export function opaqueEpisodeId(secret, sourceId, sourceSession) {
|
|
66
|
+
return createHmac("sha256", secret)
|
|
67
|
+
.update("afterhours/delegation-episode/v1\0")
|
|
68
|
+
.update(sourceId)
|
|
69
|
+
.update("\0")
|
|
70
|
+
.update(sourceSession)
|
|
71
|
+
.digest("hex")
|
|
72
|
+
.slice(0, 40);
|
|
73
|
+
}
|
|
21
74
|
function observedMinutes(events, capMinutes) {
|
|
22
75
|
const byHour = new Map();
|
|
23
76
|
const sorted = [...new Set(events)].sort((a, b) => a - b);
|
|
@@ -58,6 +111,43 @@ export function bucketsFrom(events) {
|
|
|
58
111
|
}
|
|
59
112
|
return [...rows.values()].sort((a, b) => a.hour.localeCompare(b.hour));
|
|
60
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Per-model rollup of a source's work. Minutes use the same gap-to-next rule
|
|
116
|
+
* as the activity buckets, computed over ALL agent events so a model's share
|
|
117
|
+
* is its slice of the real timeline — a per-model subset would misread the
|
|
118
|
+
* gaps between models as one model's idle time. Events that never named a
|
|
119
|
+
* model shape the gaps but credit no entry, so the rollup can honestly sum
|
|
120
|
+
* to less than the source total.
|
|
121
|
+
*/
|
|
122
|
+
export function modelActivityFrom(activityEvents, turnEvents) {
|
|
123
|
+
const modelAt = new Map();
|
|
124
|
+
for (const event of activityEvents) {
|
|
125
|
+
if (event.kind === "agent" && event.model)
|
|
126
|
+
modelAt.set(event.at, event.model);
|
|
127
|
+
}
|
|
128
|
+
const times = [...new Set(activityEvents.filter((event) => event.kind === "agent").map((event) => event.at))].sort((a, b) => a - b);
|
|
129
|
+
const rollup = new Map();
|
|
130
|
+
const entry = (model) => {
|
|
131
|
+
const existing = rollup.get(model) ?? { model, agentTurns: 0, agentActiveMinutes: 0 };
|
|
132
|
+
rollup.set(model, existing);
|
|
133
|
+
return existing;
|
|
134
|
+
};
|
|
135
|
+
for (let index = 0; index < times.length; index += 1) {
|
|
136
|
+
const at = times[index];
|
|
137
|
+
const model = modelAt.get(at);
|
|
138
|
+
if (!model)
|
|
139
|
+
continue;
|
|
140
|
+
const next = times[index + 1];
|
|
141
|
+
entry(model).agentActiveMinutes += next == null ? 1 : Math.max(1, Math.min(5, (next - at) / 60_000));
|
|
142
|
+
}
|
|
143
|
+
for (const event of turnEvents) {
|
|
144
|
+
if (event.model)
|
|
145
|
+
entry(event.model).agentTurns += 1;
|
|
146
|
+
}
|
|
147
|
+
return [...rollup.values()]
|
|
148
|
+
.map((item) => ({ ...item, agentActiveMinutes: Math.round(item.agentActiveMinutes * 100) / 100 }))
|
|
149
|
+
.sort((a, b) => b.agentActiveMinutes - a.agentActiveMinutes || a.model.localeCompare(b.model));
|
|
150
|
+
}
|
|
61
151
|
export function emptySource(id, name, configured) {
|
|
62
152
|
return {
|
|
63
153
|
id,
|
|
@@ -68,7 +158,64 @@ export function emptySource(id, name, configured) {
|
|
|
68
158
|
agentTurns: 0,
|
|
69
159
|
humanCheckIns: 0,
|
|
70
160
|
tokens: { ...EMPTY_TOKENS },
|
|
161
|
+
models: [],
|
|
71
162
|
buckets: [],
|
|
163
|
+
episodes: [],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function minutesFrom(events, kind, capMinutes) {
|
|
167
|
+
return [...observedMinutes(events.filter((event) => event.kind === kind).map((event) => event.at), capMinutes).values()].reduce((sum, value) => sum + value, 0);
|
|
168
|
+
}
|
|
169
|
+
function longestAutonomousRun(events) {
|
|
170
|
+
const sorted = [...events].sort((a, b) => a.at - b.at);
|
|
171
|
+
let current = 0;
|
|
172
|
+
let longest = 0;
|
|
173
|
+
for (let index = 0; index < sorted.length; index += 1) {
|
|
174
|
+
const event = sorted[index];
|
|
175
|
+
if (!event)
|
|
176
|
+
continue;
|
|
177
|
+
if (event.kind === "human") {
|
|
178
|
+
current = 0;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const next = sorted[index + 1];
|
|
182
|
+
current += next == null ? 1 : Math.max(1, Math.min(5, (next.at - event.at) / 60_000));
|
|
183
|
+
longest = Math.max(longest, current);
|
|
184
|
+
}
|
|
185
|
+
return Math.round(longest * 100) / 100;
|
|
186
|
+
}
|
|
187
|
+
export function delegationEpisode(input) {
|
|
188
|
+
if (input.events.length === 0)
|
|
189
|
+
return null;
|
|
190
|
+
const events = [...input.events].sort((a, b) => a.at - b.at);
|
|
191
|
+
const first = events[0];
|
|
192
|
+
const last = events.at(-1);
|
|
193
|
+
if (!first || !last)
|
|
194
|
+
return null;
|
|
195
|
+
const humanCheckIns = events.filter((event) => event.kind === "human").length;
|
|
196
|
+
const clock = localClock(first.at);
|
|
197
|
+
return {
|
|
198
|
+
id: input.id,
|
|
199
|
+
startedAt: new Date(first.at).toISOString(),
|
|
200
|
+
endedAt: new Date(last.at).toISOString(),
|
|
201
|
+
localDate: clock.localDate,
|
|
202
|
+
localStartHour: clock.localHour,
|
|
203
|
+
status: input.untilMs - last.at <= 5 * 60_000 ? "open" : "observed_end",
|
|
204
|
+
confidence: input.confidence ?? "high",
|
|
205
|
+
metricVersion: "delegation-episode-v1",
|
|
206
|
+
adapterVersion: input.adapterVersion,
|
|
207
|
+
modelFamily: input.modelFamily ?? null,
|
|
208
|
+
agentTurns: events.filter((event) => event.kind === "agent").length,
|
|
209
|
+
humanCheckIns,
|
|
210
|
+
humanReEntries: Math.max(0, humanCheckIns - 1),
|
|
211
|
+
agentActiveMinutes: Math.round(minutesFrom(events, "agent", 5) * 100) / 100,
|
|
212
|
+
humanAttentionMinutes: Math.round(minutesFrom(events, "human", 10) * 100) / 100,
|
|
213
|
+
longestAutonomousRunMinutes: longestAutonomousRun(events),
|
|
214
|
+
approvalRequests: null,
|
|
215
|
+
failureEvents: null,
|
|
216
|
+
reworkEvents: null,
|
|
217
|
+
latency: input.latency ?? { ...NO_LATENCY },
|
|
218
|
+
tokens: { ...input.tokens },
|
|
72
219
|
};
|
|
73
220
|
}
|
|
74
221
|
export function finishSource(source, events, fidelity = "activity") {
|
package/dist/activity.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity.js","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"activity.js","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAoBzC,MAAM,CAAC,MAAM,YAAY,GAAgB,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AAEtG,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACtF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACzF,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAoB,EAAE,KAAoB;IACjE,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACxE,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrE,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,EAAU,EAAE,OAAe,EAAE,OAAe;IACnE,OAAO,EAAE,IAAI,OAAO,IAAI,EAAE,IAAI,OAAO,CAAC;AACxC,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,OAAO,UAAU,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;AAClC,CAAC;AAED,SAAS,UAAU,CAAC,EAAU;IAC5B,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC;IACvB,MAAM,SAAS,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/H,MAAM,SAAS,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC/B,CAAC,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACtB,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AACpE,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAmB,EAAE,gBAAgB,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AAE1G,6EAA6E;AAC7E,MAAM,iBAAiB,GAAG,EAAE,GAAG,MAAM,CAAC;AAEtC,MAAM,UAAU,YAAY,CAAC,OAAiB;IAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACpG,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,IAAI,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC;IACxG,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;AAC/F,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAoB;IACnD,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IACvD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,cAAc,GAAkB,IAAI,CAAC;IACzC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC3B,cAAc,GAAG,KAAK,CAAC,EAAE,CAAC;YAC1B,SAAS;QACX,CAAC;QACD,IAAI,cAAc,IAAI,IAAI;YAAE,SAAS;QACrC,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,GAAG,cAAc,CAAC;QACvC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,iBAAiB;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7D,cAAc,GAAG,IAAI,CAAC;IACxB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,QAAgB,EAAE,aAAqB;IACrF,OAAO,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC;SAChC,MAAM,CAAC,oCAAoC,CAAC;SAC5C,MAAM,CAAC,QAAQ,CAAC;SAChB,MAAM,CAAC,IAAI,CAAC;SACZ,MAAM,CAAC,aAAa,CAAC;SACrB,MAAM,CAAC,KAAK,CAAC;SACb,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClB,CAAC;AAED,SAAS,eAAe,CAAC,MAAgB,EAAE,UAAkB;IAC3D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,EAAE,IAAI,IAAI;YAAE,SAAS;QACzB,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3B,MAAM,OAAO,GAAG,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;QAC3F,MAAM,GAAG,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC;QAChC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAoB;IAC9C,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxF,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxF,MAAM,YAAY,GAAG,eAAe,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC/C,MAAM,YAAY,GAAG,eAAe,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,IAAI,GAAG,EAA0B,CAAC;IAE/C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI;YAC5B,IAAI;YACJ,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,UAAU,EAAE,CAAC;YACb,aAAa,EAAE,CAAC;YAChB,kBAAkB,EAAE,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;YAC/C,qBAAqB,EAAE,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;SACnD,CAAC;QACF,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;YAAE,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC;;YAC3C,GAAG,CAAC,aAAa,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACtB,CAAC;IAED,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AACzE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAC/B,cAA4B,EAC5B,UAAwB;IAExB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;QACnC,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CACvB,cAAc,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAClF,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACzB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAC;IAChD,MAAM,KAAK,GAAG,CAAC,KAAa,EAAiB,EAAE;QAC7C,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,kBAAkB,EAAE,CAAC,EAAE,CAAC;QACtF,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC5B,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;IACF,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACrD,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAE,CAAC;QACzB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9B,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAC9B,KAAK,CAAC,KAAK,CAAC,CAAC,kBAAkB,IAAI,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IACvG,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC/B,IAAI,KAAK,CAAC,KAAK;YAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;SACxB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,kBAAkB,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,kBAAkB,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC;SACjG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,kBAAkB,GAAG,CAAC,CAAC,kBAAkB,IAAI,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AACnG,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAAY,EAAE,IAAY,EAAE,UAAmB;IACzE,OAAO;QACL,EAAE;QACF,IAAI;QACJ,UAAU;QACV,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM;QAC1C,QAAQ,EAAE,CAAC;QACX,UAAU,EAAE,CAAC;QACb,aAAa,EAAE,CAAC;QAChB,MAAM,EAAE,EAAE,GAAG,YAAY,EAAE;QAC3B,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,EAAE;QACX,QAAQ,EAAE,EAAE;KACb,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,MAAoB,EAAE,IAAwB,EAAE,UAAkB;IACrF,OAAO,CAAC,GAAG,eAAe,CACxB,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,EACtE,UAAU,CACX,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,SAAS,oBAAoB,CAAC,MAAoB;IAChD,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IACvD,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACtD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5B,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC3B,OAAO,GAAG,CAAC,CAAC;YACZ,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAC/B,OAAO,IAAI,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;QACtF,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KASjC;IACC,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACxB,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACjC,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IAC9E,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACnC,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,SAAS,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE;QAC3C,OAAO,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE;QACxC,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,cAAc,EAAE,KAAK,CAAC,SAAS;QAC/B,MAAM,EAAE,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc;QACvE,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,MAAM;QACtC,aAAa,EAAE,uBAAuB;QACtC,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI;QACtC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,MAAM;QACnE,aAAa;QACb,cAAc,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,GAAG,CAAC,CAAC;QAC9C,kBAAkB,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG;QAC3E,qBAAqB,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG;QAC/E,2BAA2B,EAAE,oBAAoB,CAAC,MAAM,CAAC;QACzD,gBAAgB,EAAE,IAAI;QACtB,aAAa,EAAE,IAAI;QACnB,YAAY,EAAE,IAAI;QAClB,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,EAAE,GAAG,UAAU,EAAE;QAC3C,MAAM,EAAE,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE;KAC5B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,MAAsB,EACtB,MAAoB,EACpB,WAAuC,UAAU;IAEjD,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IAC5E,MAAM,CAAC,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IAC/E,MAAM,CAAC,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,MAAM,CAAC,QAAQ,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACzE,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
export declare const AUTO_SYNC_WINDOW_MS: number;
|
|
3
|
+
export interface SyncStamp {
|
|
4
|
+
attemptedAt: string | null;
|
|
5
|
+
succeededAt: string | null;
|
|
6
|
+
}
|
|
7
|
+
export declare function syncStampPath(home?: string): string;
|
|
8
|
+
export declare function readSyncStamp(home?: string): SyncStamp;
|
|
9
|
+
export declare function markSyncAttempt(home?: string, now?: number): void;
|
|
10
|
+
export declare function markSyncSuccess(home?: string, now?: number): void;
|
|
11
|
+
export declare function shouldAutoSync(home?: string, now?: number, windowMs?: number): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Authoritative, child-side sync. Attempts are stamped before the network call
|
|
14
|
+
* so a failing server is retried at the window cadence, never per hook event.
|
|
15
|
+
*/
|
|
16
|
+
export declare function runAutoSync(home?: string, fetcher?: typeof fetch, now?: number): Promise<boolean>;
|
|
17
|
+
/**
|
|
18
|
+
* Cheap parent-side decision inside `trs hook`: never network I/O here — the
|
|
19
|
+
* hook must return within the agent's timeout. The child re-checks the window.
|
|
20
|
+
*/
|
|
21
|
+
export declare function maybeSpawnAutoSync(cliPath: string, home?: string, spawner?: typeof spawn, now?: number): boolean;
|
|
22
|
+
//# sourceMappingURL=autosync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autosync.d.ts","sourceRoot":"","sources":["../src/autosync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAO3C,eAAO,MAAM,mBAAmB,QAAc,CAAC;AAE/C,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAgB,aAAa,CAAC,IAAI,SAAY,GAAG,MAAM,CAEtD;AAED,wBAAgB,aAAa,CAAC,IAAI,SAAY,GAAG,SAAS,CASzD;AAUD,wBAAgB,eAAe,CAAC,IAAI,SAAY,EAAE,GAAG,SAAa,GAAG,IAAI,CAExE;AAED,wBAAgB,eAAe,CAAC,IAAI,SAAY,EAAE,GAAG,SAAa,GAAG,IAAI,CAExE;AAED,wBAAgB,cAAc,CAAC,IAAI,SAAY,EAAE,GAAG,SAAa,EAAE,QAAQ,SAAsB,GAAG,OAAO,CAW1G;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,IAAI,SAAY,EAAE,OAAO,GAAE,OAAO,KAAa,EAAE,GAAG,SAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAarH;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,IAAI,SAAY,EAChB,OAAO,GAAE,OAAO,KAAa,EAC7B,GAAG,SAAa,GACf,OAAO,CAST"}
|
package/dist/autosync.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { readCredentials, syncSnapshot } from "./cloud.js";
|
|
6
|
+
import { collectAfterhours } from "./collect.js";
|
|
7
|
+
export const AUTO_SYNC_WINDOW_MS = 45 * 60_000;
|
|
8
|
+
export function syncStampPath(home = homedir()) {
|
|
9
|
+
return join(home, ".teeras", "afterhours", "last-sync.json");
|
|
10
|
+
}
|
|
11
|
+
export function readSyncStamp(home = homedir()) {
|
|
12
|
+
try {
|
|
13
|
+
const value = JSON.parse(readFileSync(syncStampPath(home), "utf8"));
|
|
14
|
+
const iso = (candidate) => typeof candidate === "string" && Number.isFinite(Date.parse(candidate)) ? candidate : null;
|
|
15
|
+
return { attemptedAt: iso(value.attemptedAt), succeededAt: iso(value.succeededAt) };
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return { attemptedAt: null, succeededAt: null };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function writeSyncStamp(stamp, home) {
|
|
22
|
+
const path = syncStampPath(home);
|
|
23
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
24
|
+
chmodSync(dirname(path), 0o700);
|
|
25
|
+
writeFileSync(path, `${JSON.stringify(stamp, null, 2)}\n`, { mode: 0o600 });
|
|
26
|
+
chmodSync(path, 0o600);
|
|
27
|
+
}
|
|
28
|
+
export function markSyncAttempt(home = homedir(), now = Date.now()) {
|
|
29
|
+
writeSyncStamp({ ...readSyncStamp(home), attemptedAt: new Date(now).toISOString() }, home);
|
|
30
|
+
}
|
|
31
|
+
export function markSyncSuccess(home = homedir(), now = Date.now()) {
|
|
32
|
+
writeSyncStamp({ ...readSyncStamp(home), succeededAt: new Date(now).toISOString() }, home);
|
|
33
|
+
}
|
|
34
|
+
export function shouldAutoSync(home = homedir(), now = Date.now(), windowMs = AUTO_SYNC_WINDOW_MS) {
|
|
35
|
+
if (!readCredentials(home))
|
|
36
|
+
return false;
|
|
37
|
+
const stamp = readSyncStamp(home);
|
|
38
|
+
// Manual commands (`setup`, `connect`, `login`, `sync`, `week`) stamp success
|
|
39
|
+
// only, so the window has to answer to whichever happened last — otherwise a
|
|
40
|
+
// hook event seconds after `trs setup` spawns a redundant sync.
|
|
41
|
+
const times = [stamp.attemptedAt, stamp.succeededAt]
|
|
42
|
+
.map((value) => (value ? Date.parse(value) : null))
|
|
43
|
+
.filter((value) => value != null);
|
|
44
|
+
if (times.length === 0)
|
|
45
|
+
return true;
|
|
46
|
+
return now - Math.max(...times) >= windowMs;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Authoritative, child-side sync. Attempts are stamped before the network call
|
|
50
|
+
* so a failing server is retried at the window cadence, never per hook event.
|
|
51
|
+
*/
|
|
52
|
+
export async function runAutoSync(home = homedir(), fetcher = fetch, now = Date.now()) {
|
|
53
|
+
if (!shouldAutoSync(home, now))
|
|
54
|
+
return false;
|
|
55
|
+
const credentials = readCredentials(home);
|
|
56
|
+
if (!credentials)
|
|
57
|
+
return false;
|
|
58
|
+
markSyncAttempt(home, now);
|
|
59
|
+
try {
|
|
60
|
+
await syncSnapshot(collectAfterhours({ home }), credentials, fetcher);
|
|
61
|
+
markSyncSuccess(home, now);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Failure is visible via `trs sources` (last attempt vs last success).
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Cheap parent-side decision inside `trs hook`: never network I/O here — the
|
|
71
|
+
* hook must return within the agent's timeout. The child re-checks the window.
|
|
72
|
+
*/
|
|
73
|
+
export function maybeSpawnAutoSync(cliPath, home = homedir(), spawner = spawn, now = Date.now()) {
|
|
74
|
+
if (!shouldAutoSync(home, now))
|
|
75
|
+
return false;
|
|
76
|
+
try {
|
|
77
|
+
const child = spawner(process.execPath, [cliPath, "autosync"], { detached: true, stdio: "ignore" });
|
|
78
|
+
child.unref();
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=autosync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autosync.js","sourceRoot":"","sources":["../src/autosync.ts"],"names":[],"mappings":"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"}
|