zeno-cli 0.3.4__py3-none-any.whl
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.
- zeno_adapters/__init__.py +17 -0
- zeno_adapters/_common.py +38 -0
- zeno_adapters/anthropic.py +68 -0
- zeno_adapters/claude_code.py +101 -0
- zeno_adapters/crewai.py +92 -0
- zeno_adapters/langgraph.py +49 -0
- zeno_adapters/openai.py +108 -0
- zeno_cli/__init__.py +1 -0
- zeno_cli/_hooks/cc_bridge.py +1016 -0
- zeno_cli/doctor.py +535 -0
- zeno_cli/hook_install.py +269 -0
- zeno_cli/hud/__init__.py +1 -0
- zeno_cli/hud/hud_install.py +652 -0
- zeno_cli/hud/zeno_attention.py +288 -0
- zeno_cli/hud/zeno_cognition.py +457 -0
- zeno_cli/hud/zeno_hud.py +496 -0
- zeno_cli/interview_invites.py +342 -0
- zeno_cli/login.py +241 -0
- zeno_cli/main.py +2534 -0
- zeno_cli/onboard.py +206 -0
- zeno_cli/outreach.py +456 -0
- zeno_cli/version.py +67 -0
- zeno_cli-0.3.4.dist-info/METADATA +161 -0
- zeno_cli-0.3.4.dist-info/RECORD +69 -0
- zeno_cli-0.3.4.dist-info/WHEEL +4 -0
- zeno_cli-0.3.4.dist-info/entry_points.txt +4 -0
- zeno_core/__init__.py +67 -0
- zeno_core/analytics.py +193 -0
- zeno_core/rtlx_s.py +460 -0
- zeno_core/streak.py +178 -0
- zeno_core/tlx_s.py +192 -0
- zeno_sdk/__init__.py +6 -0
- zeno_sdk/_generated/__init__.py +6 -0
- zeno_sdk/_generated/client.py +819 -0
- zeno_sdk/_migrations/alembic/env.py +33 -0
- zeno_sdk/_migrations/alembic/script.py.mako +18 -0
- zeno_sdk/_migrations/alembic/versions/0001_initial.py +79 -0
- zeno_sdk/_migrations/alembic/versions/0002_cognition_samples.py +53 -0
- zeno_sdk/_migrations/alembic/versions/0003_cognition_drivers.py +41 -0
- zeno_sdk/_migrations/alembic/versions/0004_transcript_intelligence.py +248 -0
- zeno_sdk/_migrations/alembic.ini +35 -0
- zeno_sdk/_runtime.py +12 -0
- zeno_sdk/adapters/__init__.py +15 -0
- zeno_sdk/adapters/anthropic.py +5 -0
- zeno_sdk/adapters/claude_code.py +5 -0
- zeno_sdk/adapters/crewai.py +5 -0
- zeno_sdk/adapters/langgraph.py +5 -0
- zeno_sdk/adapters/openai.py +5 -0
- zeno_sdk/auth.py +25 -0
- zeno_sdk/client.py +87 -0
- zeno_sdk/config.py +61 -0
- zeno_sdk/daemon.py +72 -0
- zeno_sdk/privacy.py +46 -0
- zeno_sdk/session.py +179 -0
- zeno_sdk/storage.py +487 -0
- zeno_sdk/types/__init__.py +121 -0
- zeno_session_intel/__init__.py +19 -0
- zeno_session_intel/analytics.py +588 -0
- zeno_session_intel/compression.py +123 -0
- zeno_session_intel/ingest.py +376 -0
- zeno_session_intel/model.py +129 -0
- zeno_session_intel/parsers/__init__.py +31 -0
- zeno_session_intel/parsers/claude_code.py +169 -0
- zeno_session_intel/parsers/codex.py +265 -0
- zeno_session_intel/parsers/cursor.py +198 -0
- zeno_session_intel/prices.py +281 -0
- zeno_session_intel/schema.py +277 -0
- zeno_session_intel/signals.py +319 -0
- zeno_session_intel/taxonomy.py +71 -0
zeno_cli/version.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""CLI version + git-SHA helpers.
|
|
2
|
+
|
|
3
|
+
Surfaced via `zeno --version` and `zeno version`. The version string is the
|
|
4
|
+
zeno-cli pyproject.toml version (today: 0.1.0), resolved at runtime via
|
|
5
|
+
importlib.metadata so a wheel install and the editable workspace agree. The
|
|
6
|
+
git SHA is best-effort: if the repo is reachable via git in the working tree
|
|
7
|
+
above this file, we surface a short SHA; otherwise we omit it cleanly.
|
|
8
|
+
|
|
9
|
+
Used by `zeno doctor` too - the first row in the diagnostic is the version
|
|
10
|
+
banner so support requests get a fingerprint.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import subprocess
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def package_version() -> str:
|
|
20
|
+
"""Return the installed zeno-cli version, or "0.0.0+unknown" on failure.
|
|
21
|
+
|
|
22
|
+
Uses ``importlib.metadata`` so an editable install (uv workspace) sees
|
|
23
|
+
the same version as a wheel install. Falls back to a sentinel string
|
|
24
|
+
when zeno-cli isn't actually installed (e.g. running from source via
|
|
25
|
+
PYTHONPATH only - the workspace test setup).
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
from importlib.metadata import version # noqa: PLC0415
|
|
29
|
+
|
|
30
|
+
return version("zeno-cli")
|
|
31
|
+
except Exception:
|
|
32
|
+
return "0.0.0+unknown"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def git_sha(short: bool = True, max_depth: int = 8) -> str | None:
|
|
36
|
+
"""Return the short git SHA of the working tree, or None if unavailable.
|
|
37
|
+
|
|
38
|
+
Walks up from this file looking for a .git directory so the SHA reflects
|
|
39
|
+
the source tree the CLI was launched from (important for editable
|
|
40
|
+
installs from a uv workspace). Returns None for installed wheels in
|
|
41
|
+
site-packages where there is no .git nearby.
|
|
42
|
+
"""
|
|
43
|
+
here = Path(__file__).resolve()
|
|
44
|
+
for parent in [here.parent, *here.parents][:max_depth]:
|
|
45
|
+
if (parent / ".git").exists():
|
|
46
|
+
try:
|
|
47
|
+
fmt = "--short" if short else "--verify"
|
|
48
|
+
out = subprocess.run(
|
|
49
|
+
["git", "rev-parse", fmt, "HEAD"],
|
|
50
|
+
capture_output=True,
|
|
51
|
+
text=True,
|
|
52
|
+
timeout=2,
|
|
53
|
+
cwd=str(parent),
|
|
54
|
+
)
|
|
55
|
+
if out.returncode == 0:
|
|
56
|
+
return out.stdout.strip() or None
|
|
57
|
+
except (OSError, subprocess.SubprocessError):
|
|
58
|
+
return None
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def version_string() -> str:
|
|
63
|
+
"""One-line version banner: '<pkg-version> (<sha>)' or '<pkg-version>'."""
|
|
64
|
+
sha = git_sha()
|
|
65
|
+
if sha:
|
|
66
|
+
return f"{package_version()} ({sha})"
|
|
67
|
+
return package_version()
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: zeno-cli
|
|
3
|
+
Version: 0.3.4
|
|
4
|
+
Summary: Zeno CLI - measure the supervision cost of AI-assisted work
|
|
5
|
+
Project-URL: Homepage, https://zeno.center
|
|
6
|
+
Project-URL: Repository, https://github.com/Marwan01/zeno
|
|
7
|
+
Author-email: Mar Helali <mar@1dev.space>
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
Keywords: ai,claude-code,cognitive-load,rtlx-s,session-analytics,supervision
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Software Development
|
|
16
|
+
Requires-Python: >=3.12
|
|
17
|
+
Requires-Dist: aiosqlite>=0.20.0
|
|
18
|
+
Requires-Dist: alembic>=1.13.2
|
|
19
|
+
Requires-Dist: keyring>=25.3.0
|
|
20
|
+
Requires-Dist: matplotlib>=3.9.2
|
|
21
|
+
Requires-Dist: numpy>=2.0
|
|
22
|
+
Requires-Dist: pydantic>=2.9.0
|
|
23
|
+
Requires-Dist: rich>=13.8.1
|
|
24
|
+
Requires-Dist: scipy>=1.14.1
|
|
25
|
+
Requires-Dist: textual>=0.86.1
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# zeno-cli
|
|
29
|
+
|
|
30
|
+
Measure the supervision cost of AI-assisted work, from your terminal. `zeno`
|
|
31
|
+
records RTLX-S load probes + session activity, fits your personal "babysitting
|
|
32
|
+
tax" curve, and surfaces where your optimal number of parallel agents sits.
|
|
33
|
+
|
|
34
|
+
## Install
|
|
35
|
+
|
|
36
|
+
Install the `zeno` CLI straight from the monorepo over SSH. It is one
|
|
37
|
+
self-contained package: the SDK, core, adapters, session-intelligence engine,
|
|
38
|
+
and SQLite migrations all ship inside it. Requires **Python >= 3.12** and GitHub
|
|
39
|
+
SSH access to the repo.
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
uv tool install \
|
|
43
|
+
--from "git+ssh://git@github.com/Marwan01/zeno.git@cli-v0.3.3#subdirectory=apps/cli" \
|
|
44
|
+
zeno-cli
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`pipx install "git+ssh://git@github.com/Marwan01/zeno.git@cli-v0.3.3#subdirectory=apps/cli"`
|
|
48
|
+
works the same way. The git clone runs an in-tree build, so the hatchling build
|
|
49
|
+
hook (`apps/cli/hatch_build.py`) vendors the workspace packages into the wheel.
|
|
50
|
+
**Update** by bumping the tag: re-run with `@cli-v0.3.3` (uv) or
|
|
51
|
+
`pipx install --force ...@<newtag>`.
|
|
52
|
+
|
|
53
|
+
**Why git+ssh and not an index:** TestPyPI is not a durable distribution channel
|
|
54
|
+
and a private index is overkill at team scale. A public PyPI wheel comes only
|
|
55
|
+
when non-repo-access users do. The packaging blocker for it is now resolved - the
|
|
56
|
+
build is sdist-clean (`uv build --sdist` -> a wheel built from that sdist installs
|
|
57
|
+
and runs every verb; see the build-hook note under [Packaging](#packaging-notes)),
|
|
58
|
+
so the remaining gate is purely "do public users exist yet", not a broken build.
|
|
59
|
+
|
|
60
|
+
### Install channels (staged)
|
|
61
|
+
|
|
62
|
+
The distribution channel widens as the audience does. Use the one that matches
|
|
63
|
+
where the project is, not the most convenient:
|
|
64
|
+
|
|
65
|
+
| Stage | Channel | Command | Status |
|
|
66
|
+
| :--- | :--- | :--- | :--- |
|
|
67
|
+
| Now | git+ssh tag | `uv tool install --from "git+ssh://git@github.com/Marwan01/zeno.git@cli-v0.3.3#subdirectory=apps/cli" zeno-cli` | Live (needs repo SSH access) |
|
|
68
|
+
| Next | PyPI | `uv tool install zeno-cli` (or `pipx install zeno-cli`) | Lands when `.github/workflows/publish.yml` runs on the first GitHub release; the packaging metadata is ready |
|
|
69
|
+
| Later | Homebrew | `brew install zeno-cli` | Formula not written yet |
|
|
70
|
+
|
|
71
|
+
The PyPI publish is OIDC Trusted-Publishing gated and fires only on a published
|
|
72
|
+
GitHub release - see `.github/workflows/publish.yml`. Until that first release,
|
|
73
|
+
the git+ssh tag is the supported path.
|
|
74
|
+
|
|
75
|
+
**From a built wheel** (offline / air-gapped; the wheel is fully self-contained):
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
uv build --wheel --package zeno-cli # -> dist/zeno_cli-*.whl
|
|
79
|
+
uv tool install ./dist/zeno_cli-0.3.3-py3-none-any.whl
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**From source** (development inside the monorepo, no install):
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
uv run --project /path/to/zeno zeno <cmd>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
New here? Follow `docs/ADOPT.md` for the 10-minute install -> capture -> survey loop.
|
|
89
|
+
|
|
90
|
+
## Use
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
zeno survey # log how a session felt (~6s, RTLX-S 5-item probe)
|
|
94
|
+
zeno status # tier, streak, tax curve, cognitive state, today's survey
|
|
95
|
+
zeno curve # render the babysitting-tax curve (needs ~10 sessions of data)
|
|
96
|
+
zeno weekly # 7-day rollup
|
|
97
|
+
zeno doctor # health check: API reachability + identity
|
|
98
|
+
zeno login # browser sign-in; stores a Clerk JWT in the OS keyring
|
|
99
|
+
zeno whoami # show the identity + tier the API sees for your token
|
|
100
|
+
zeno logout # clear the stored token
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Authentication
|
|
104
|
+
|
|
105
|
+
`zeno login` runs a browser + loopback sign-in and stores the resulting Clerk
|
|
106
|
+
JWT in your OS keyring; the CLI sends it as a `Bearer` token so the API
|
|
107
|
+
attributes requests to your user. Token resolution order, used by every
|
|
108
|
+
command: `ZENO_API_TOKEN` env override first, then the keyring token from
|
|
109
|
+
`zeno login`, then none (local-only). Use `zeno login --no-browser` for SSH /
|
|
110
|
+
headless (prints the URL), and `--authorize-url` / `ZENO_LOGIN_AUTHORIZE_URL`
|
|
111
|
+
to target a non-default bridge.
|
|
112
|
+
|
|
113
|
+
> The dashboard `/cli/authorize` bridge page + the Clerk `zeno-api` JWT template
|
|
114
|
+
> that mint the token are a documented follow-up (not yet shipped). See
|
|
115
|
+
> `src/zeno_cli/login.py` for the callback contract.
|
|
116
|
+
|
|
117
|
+
Full daily-loop guide: `docs/CLI_QUICKSTART.md`. Requires Python >= 3.12.
|
|
118
|
+
|
|
119
|
+
## Cognition HUD add-on
|
|
120
|
+
|
|
121
|
+
`zeno-hud-bar` is the single differentiated cognition line (`att / eff / drv`) that
|
|
122
|
+
stacks UNDER whatever popular HUD you already run, instead of replacing it. Claude Code
|
|
123
|
+
has one statusLine slot, so zeno rides the HUD you have:
|
|
124
|
+
|
|
125
|
+
```sh
|
|
126
|
+
zeno hud install # auto-detect: ccstatusline (native widget) or claude-hud (wrapper)
|
|
127
|
+
zeno hud install --dry-run # print what would change, write nothing
|
|
128
|
+
zeno hud status # what is detected + whether the line is wired in
|
|
129
|
+
zeno hud uninstall # remove from both adapters (idempotent; --restore for byte-for-byte)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`--target {auto,ccstatusline,claude-hud}` forces an adapter (auto prefers ccstatusline
|
|
133
|
+
when both are present). Every edit is backed up and reversible. The bar is crash-safe: on
|
|
134
|
+
any error it prints an empty line and exits 0, so it never breaks the host HUD. Full model
|
|
135
|
+
and both recipes: `docs/HUD_ADDON.md`.
|
|
136
|
+
|
|
137
|
+
## Packaging notes
|
|
138
|
+
|
|
139
|
+
- **sdist-clean via a build hook.** `apps/cli/hatch_build.py` (a custom hatchling
|
|
140
|
+
build hook, enabled on BOTH the `wheel` and `sdist` targets) vendors the four
|
|
141
|
+
workspace packages (`zeno_core` / `zeno_sdk` / `zeno_adapters` /
|
|
142
|
+
`zeno_session_intel`) plus the SDK alembic tree into an in-root, gitignored
|
|
143
|
+
`_vendor_build/` stage at build time. The wheel target flattens them to the
|
|
144
|
+
archive top level (the exact layout the old static `force-include` produced, so
|
|
145
|
+
absolute imports resolve unchanged); the sdist target ships the whole
|
|
146
|
+
`_vendor_build/` stage inside the tarball so a wheel built FROM the sdist finds
|
|
147
|
+
the sources locally. This replaced the static `[tool.hatch...force-include]` of
|
|
148
|
+
`../../packages/...` relative-parent paths, whose `..` escaped the sdist root and
|
|
149
|
+
were silently dropped - a wheel from that sdist died with "Forced include not
|
|
150
|
+
found". Repo source under `packages/*` is never moved, so the uv workspace, the
|
|
151
|
+
API, and the test suite keep importing the editable members unchanged; the
|
|
152
|
+
vendored copy is purely a build artifact.
|
|
153
|
+
- `zeno ingest` / `zeno usage` are backed by `zeno_session_intel`, vendored
|
|
154
|
+
alongside the other internal packages by the build hook. It is stdlib-only with
|
|
155
|
+
relative-only internal imports, so it flattens into the wheel with zero import
|
|
156
|
+
rewrites. Omit it and both subcommands abort with a "not bundled" hint at runtime
|
|
157
|
+
(the regression fixed in 0.3.3); `scripts/install-smoke.sh` now exercises every
|
|
158
|
+
advertised subcommand so this cannot ship silently again.
|
|
159
|
+
- Agent-framework adapters (`zeno_sdk.adapters` -> crewai / openai / anthropic /
|
|
160
|
+
langgraph) are off the CLI path and pull heavy framework deps; they are kept
|
|
161
|
+
optional and will be exposed as extras (e.g. `zeno-cli[crewai]`) in a later stage.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
zeno_cli/__init__.py,sha256=k_rgP6JpTT711x3-8Pss91qDM-W8iE212qb98h8yL8w,36
|
|
2
|
+
zeno_cli/doctor.py,sha256=2fYpTBiZRT7Oxmc0vJs5N_nxRXfPwDUsmRs5GE6oQpY,21565
|
|
3
|
+
zeno_cli/hook_install.py,sha256=2T_R-cpe_TdjJMjjye4IFr4G1I0GldIgxCoCQqL5Te4,9679
|
|
4
|
+
zeno_cli/interview_invites.py,sha256=7c9PhjTNsRdhvsK-dibEtfH-SIgIgDemJyAzZ0wdFSU,12276
|
|
5
|
+
zeno_cli/login.py,sha256=0TrlNlumx4vTDxNdREE1FjEJPevZRTZVvBUMmIcyjVU,9727
|
|
6
|
+
zeno_cli/main.py,sha256=jexg-7f0IHEhwZMHuNaxeGSZivJvTfC-2Npip5s_UeY,94823
|
|
7
|
+
zeno_cli/onboard.py,sha256=3c8NwAvuaFf_uDBgnwvLPKgPoh2kKg3xO74fEJR9YYs,8505
|
|
8
|
+
zeno_cli/outreach.py,sha256=GJo1FeMhZQ2yhlZ10qKC0aNNAz2AC7DDfcQ-Eke9DuE,15691
|
|
9
|
+
zeno_cli/version.py,sha256=n8mZkywYtXEOSR2YnohJ3gLJ_hcxLgPU6F5Oj_2jFgo,2459
|
|
10
|
+
zeno_cli/_hooks/cc_bridge.py,sha256=EgOszKaREn1T0x0SfR1LRCMB32xa5jD144UebAlT8ZQ,40531
|
|
11
|
+
zeno_cli/hud/__init__.py,sha256=leofDy-tpR_QI7fLtKtHt3afDR3uGtOM_0YIWJjEdnA,81
|
|
12
|
+
zeno_cli/hud/hud_install.py,sha256=vWGa0CGsgMzasSW6WYvfP4IrF5P9Rku9qlb7esX8Vbk,25519
|
|
13
|
+
zeno_cli/hud/zeno_attention.py,sha256=vg-_UIAkAiNLd32JfLlQo_4-5yMsEDdMPd8eSauDI_E,11216
|
|
14
|
+
zeno_cli/hud/zeno_cognition.py,sha256=WuFCMH8RVLQegQlnqcjBoijqKeZ5uym35h-tX3fSMEM,18505
|
|
15
|
+
zeno_cli/hud/zeno_hud.py,sha256=J9oxTIsua8p6qfusjlyGO8ly_WinUnpp4rWPGaqL-34,20301
|
|
16
|
+
zeno_adapters/__init__.py,sha256=KQT3Gf4ihdqsIOPBo5jaZeamv8pC3xim0wr_7Z7SZz8,575
|
|
17
|
+
zeno_adapters/_common.py,sha256=s9Vlr0yZDIAka8xiy947UNxnmBjU-XbyWdLzx6FCG18,1165
|
|
18
|
+
zeno_adapters/anthropic.py,sha256=lAOfpnGzKbzn5MLDULDVWXxy_VwIx8YYYpBa4rHAMDo,2373
|
|
19
|
+
zeno_adapters/claude_code.py,sha256=o0xlv3lxA6caiOZTrRZVbUlpDHzLBDKzE-mBWvlctYQ,3086
|
|
20
|
+
zeno_adapters/crewai.py,sha256=YHVNg0vE9v8JEUP0H3-bhpkj_fVnqqBbqD1AaDiomYc,3413
|
|
21
|
+
zeno_adapters/langgraph.py,sha256=TzTgCG5iGhAa_wIMJ9XEllvfgyUvplOVT46MnCjQchE,1806
|
|
22
|
+
zeno_adapters/openai.py,sha256=mGIwkqgNiIhMrPeQ2fJRL-chj55RyrxTSobBC3yUKbA,3607
|
|
23
|
+
zeno_core/__init__.py,sha256=nPS4Sc_J6RVCHkaLtschKWi0DokrKS4zUW1K_gEJAcI,1510
|
|
24
|
+
zeno_core/analytics.py,sha256=GV44S1YjDrON3kU6OwshSRONSQTEVcWkijrgD1AALjw,6595
|
|
25
|
+
zeno_core/rtlx_s.py,sha256=TQABm29VCLDxSbzQRq4bKmlPqSysYHQniw85klItHOM,19217
|
|
26
|
+
zeno_core/streak.py,sha256=lQKHL4jXr53LrWebRqR1C2mfsrlacaV4qJfP15b-XeE,6826
|
|
27
|
+
zeno_core/tlx_s.py,sha256=uCBRFaMMwEFNpmbUY00EOWYEf-4-_toitv80AiZNB9o,6154
|
|
28
|
+
zeno_sdk/__init__.py,sha256=zpIpXr9s0JrctbykFiEBxFSQZMdRQvpopAUt-c2Cngw,127
|
|
29
|
+
zeno_sdk/_runtime.py,sha256=HaIO3tlu1bXJNfYB10KubuDtQoWhUZTEhH_uyhQMM18,348
|
|
30
|
+
zeno_sdk/auth.py,sha256=5uvKCeMSslAGI7jcdVHGq8bjMwwV_kW18LvcoQ1f94I,499
|
|
31
|
+
zeno_sdk/client.py,sha256=WNmBygABgeZSo01anqbTkh4CygUX3s4fbl-99TuMTXQ,2759
|
|
32
|
+
zeno_sdk/config.py,sha256=ERnRdgqyl2o7OXJm5tV3YymTHkiR0Q8yTyUrTX_fD_c,1908
|
|
33
|
+
zeno_sdk/daemon.py,sha256=FdY-_df9vRM54uKGcdKYCXUabUmSNs4AH0f0MTEDTXw,2258
|
|
34
|
+
zeno_sdk/privacy.py,sha256=P4OuxwgfMOVvZfEkTAP15GZm5156wTM3vNO-l8P8tZI,1374
|
|
35
|
+
zeno_sdk/session.py,sha256=aQ6G9fOnnLtDLzGGyqu_2mgHO7JBieseY8TyCbSlpJo,6100
|
|
36
|
+
zeno_sdk/storage.py,sha256=3wf2ED8TeTNIrnQmQlJRTXIgLDVcp266grcaVOVEH4o,19076
|
|
37
|
+
zeno_sdk/_generated/__init__.py,sha256=LyDCZa0IW5FIpGsSH8j9owFzuVepSgycbx47T4w-5QQ,96
|
|
38
|
+
zeno_sdk/_generated/client.py,sha256=5Abp7n_ST0AsPwsY6BVUhOgkZib3sRnFWfwSmgkp0pc,26729
|
|
39
|
+
zeno_sdk/_migrations/alembic.ini,sha256=0gpaG_bxrMvXtPuM9eUlEbl6mD0BqwvglNZ52PxL-O4,526
|
|
40
|
+
zeno_sdk/_migrations/alembic/env.py,sha256=iz-0Ta1jTqgiZa42zczrrHWZgucF8I6TlKOiaqUB2LU,866
|
|
41
|
+
zeno_sdk/_migrations/alembic/script.py.mako,sha256=Jc9jK0i7Tb0UW4FKhYeffle1wcBwJklOJ0hO3EYyyUY,325
|
|
42
|
+
zeno_sdk/_migrations/alembic/versions/0001_initial.py,sha256=jw7WHmoPTWSu-nWj5xyU4nIJsdIywkGww4-NSPtYK90,3061
|
|
43
|
+
zeno_sdk/_migrations/alembic/versions/0002_cognition_samples.py,sha256=aOd76chDv36T9N4slvESE_TrHdCC5oMQjzqH67x2yF4,2151
|
|
44
|
+
zeno_sdk/_migrations/alembic/versions/0003_cognition_drivers.py,sha256=OqLzyFX3ri2BrL9kE8ZXaNtD5nqxqtURaHNmYPG93Ig,1198
|
|
45
|
+
zeno_sdk/_migrations/alembic/versions/0004_transcript_intelligence.py,sha256=vMkWSGI-WpZYV5nMioQW1rAnVgUdxVrnYg5kyCr0QNM,12724
|
|
46
|
+
zeno_sdk/adapters/__init__.py,sha256=qF1-El8YWrdQjYJpOyvOcZa3FdwoCvRGYQWyRjk71Bk,543
|
|
47
|
+
zeno_sdk/adapters/anthropic.py,sha256=OMZ8-UEXIHH5KoD9SKcQNX2B66oJU6M5KZGz9d90e7I,109
|
|
48
|
+
zeno_sdk/adapters/claude_code.py,sha256=gvXAJiS4q9bixsz33SMhFKASw1vVAOVQcAUUY8lkKE4,111
|
|
49
|
+
zeno_sdk/adapters/crewai.py,sha256=yeR5ihDZYrXfjw5Bg8fG_3QTHjQwE_0NDayELBHqAPo,154
|
|
50
|
+
zeno_sdk/adapters/langgraph.py,sha256=zWbVzdkU3zdkJwEqOzj1H652ECXNq0gRC_aSLFM0EXg,109
|
|
51
|
+
zeno_sdk/adapters/openai.py,sha256=EwIyTBTFtXOxuHbA31WNkMoQV4jdqukb8XMlpz-UiVg,106
|
|
52
|
+
zeno_sdk/types/__init__.py,sha256=knlgMTaeZ4gDBLpuAC4_mFvQ_oFktO9co9-XhfX1emQ,3267
|
|
53
|
+
zeno_session_intel/__init__.py,sha256=hPlu9-Qwxe2hlevZW6kQs_pgEzJqZWspXNNHy7zNt_8,880
|
|
54
|
+
zeno_session_intel/analytics.py,sha256=gw68B-h9nO7nkmg5VO3ue00Giqhq4vskJCP5D6DkNH4,21639
|
|
55
|
+
zeno_session_intel/compression.py,sha256=3q1NklgUrhC2K47dNmydqvGnnn59KRbwQBSnvdyCM6g,5066
|
|
56
|
+
zeno_session_intel/ingest.py,sha256=KAdoxK6yZnOicYrFFQxJR5O1ONd9kH1OOBPdQi2cPgg,15030
|
|
57
|
+
zeno_session_intel/model.py,sha256=jP2cocAVjl_MlBI0bralV_TFUkalo-PRP480aLjH9FY,4235
|
|
58
|
+
zeno_session_intel/prices.py,sha256=uMxVYL6N4HqK1_usJuEeXWtlDkmZ_wMRtF6yNiC67Bw,9905
|
|
59
|
+
zeno_session_intel/schema.py,sha256=VTmUWvSXB7zgH_zZpnEFdjqJ2fIQ1pQds3OX0oKMTXo,12376
|
|
60
|
+
zeno_session_intel/signals.py,sha256=JrInX4_QiOhPQfrky_OMHwjdb_lip-w44h-E_Xua1bg,11539
|
|
61
|
+
zeno_session_intel/taxonomy.py,sha256=u_9COlFHeI3sMg0Nqd_kg2sFfDjdQwx6R-OW0vA7iCQ,2107
|
|
62
|
+
zeno_session_intel/parsers/__init__.py,sha256=rEzx44_To5fZ4IeblMio7Dxx29w9vAEn6YjRTWEf9yM,1323
|
|
63
|
+
zeno_session_intel/parsers/claude_code.py,sha256=SeiT9jrolIsCvSxXUlb4fUWzHEWLekqA4-b6MY4hXH0,6405
|
|
64
|
+
zeno_session_intel/parsers/codex.py,sha256=5qn28QItZMiiDY9OvjfuRrgNfUozpcThQngZqueVqp8,10220
|
|
65
|
+
zeno_session_intel/parsers/cursor.py,sha256=DX1DZHlu1DxzWTGrDnVc4GMf52KdV8MtlwbO_ryix34,7352
|
|
66
|
+
zeno_cli-0.3.4.dist-info/METADATA,sha256=OIBlzm7x6cizBQ37C3tfrwYOUO2SGcMwKWg63wF190g,7792
|
|
67
|
+
zeno_cli-0.3.4.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
68
|
+
zeno_cli-0.3.4.dist-info/entry_points.txt,sha256=e4KAP7N_td8T76VwMcY_Ey0eCz2L8zAO_mpeUKr7CAQ,128
|
|
69
|
+
zeno_cli-0.3.4.dist-info/RECORD,,
|
zeno_core/__init__.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Core analytics package."""
|
|
2
|
+
|
|
3
|
+
from .analytics import (
|
|
4
|
+
CurveResult,
|
|
5
|
+
SessionPoint,
|
|
6
|
+
fit_babysitting_tax_curve,
|
|
7
|
+
render_curve_ascii,
|
|
8
|
+
render_weekly_table,
|
|
9
|
+
save_curve_png,
|
|
10
|
+
weekly_summary,
|
|
11
|
+
)
|
|
12
|
+
from .rtlx_s import (
|
|
13
|
+
RTLXS_ITEMS,
|
|
14
|
+
RTLXS_SCHEMA_VERSION,
|
|
15
|
+
RTLXResponse,
|
|
16
|
+
load_last_defaults,
|
|
17
|
+
run_rtlxs_survey_tui,
|
|
18
|
+
save_last_defaults,
|
|
19
|
+
validate_item_keys,
|
|
20
|
+
)
|
|
21
|
+
from .streak import (
|
|
22
|
+
DAY_CUTOFF_HOUR,
|
|
23
|
+
StreakResult,
|
|
24
|
+
compute_streak,
|
|
25
|
+
has_logged_today,
|
|
26
|
+
read_streak_from_local_storage,
|
|
27
|
+
)
|
|
28
|
+
from .tlx_s import (
|
|
29
|
+
SUBSCALES,
|
|
30
|
+
TLXSurveyResult,
|
|
31
|
+
compute_babysitting_tax_index,
|
|
32
|
+
compute_composite_load,
|
|
33
|
+
run_tlx_survey_tui,
|
|
34
|
+
should_prompt_probe,
|
|
35
|
+
validate_item_wording,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"SUBSCALES",
|
|
40
|
+
"TLXSurveyResult",
|
|
41
|
+
"CurveResult",
|
|
42
|
+
"SessionPoint",
|
|
43
|
+
"fit_babysitting_tax_curve",
|
|
44
|
+
"render_curve_ascii",
|
|
45
|
+
"render_weekly_table",
|
|
46
|
+
"save_curve_png",
|
|
47
|
+
"weekly_summary",
|
|
48
|
+
"compute_babysitting_tax_index",
|
|
49
|
+
"compute_composite_load",
|
|
50
|
+
"run_tlx_survey_tui",
|
|
51
|
+
"should_prompt_probe",
|
|
52
|
+
"validate_item_wording",
|
|
53
|
+
# RTLX-S (research 1, 2026-06-07 PM3)
|
|
54
|
+
"RTLXS_ITEMS",
|
|
55
|
+
"RTLXS_SCHEMA_VERSION",
|
|
56
|
+
"RTLXResponse",
|
|
57
|
+
"load_last_defaults",
|
|
58
|
+
"run_rtlxs_survey_tui",
|
|
59
|
+
"save_last_defaults",
|
|
60
|
+
"validate_item_keys",
|
|
61
|
+
# Streak tracker (research 1 PM3 follow-up, 2026-06-07)
|
|
62
|
+
"DAY_CUTOFF_HOUR",
|
|
63
|
+
"StreakResult",
|
|
64
|
+
"compute_streak",
|
|
65
|
+
"has_logged_today",
|
|
66
|
+
"read_streak_from_local_storage",
|
|
67
|
+
]
|
zeno_core/analytics.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from datetime import UTC, datetime, timedelta
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
# Resolves the np.ndarray annotations below for type-checkers / ruff
|
|
11
|
+
# WITHOUT a runtime import (these names are strings under
|
|
12
|
+
# `from __future__ import annotations`).
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
# numpy / scipy / matplotlib (and rich) are imported lazily INSIDE the functions
|
|
16
|
+
# that use them, not at module top. Importing zeno_core.analytics used to pull
|
|
17
|
+
# the full scientific stack (~185ms: matplotlib.pyplot alone ~70ms, plus numpy
|
|
18
|
+
# and scipy), which every `zeno --version` / `status` / `tips` paid even though
|
|
19
|
+
# those paths never fit or plot a curve. Deferring keeps the hot path cheap;
|
|
20
|
+
# the libs load once, on first call, and are cached in sys.modules thereafter.
|
|
21
|
+
# (`from __future__ import annotations` makes the np.ndarray signatures below
|
|
22
|
+
# strings, so they do not trigger an import at definition time.)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(slots=True)
|
|
26
|
+
class SessionPoint:
|
|
27
|
+
session_id: str
|
|
28
|
+
n_agents_active: float
|
|
29
|
+
composite_load: float
|
|
30
|
+
output_quality: float | None
|
|
31
|
+
ended_at: datetime | None = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(slots=True)
|
|
35
|
+
class CurveResult:
|
|
36
|
+
xs: list[float]
|
|
37
|
+
composite_curve: list[float]
|
|
38
|
+
quality_curve: list[float]
|
|
39
|
+
optimal_n_agents: int | None
|
|
40
|
+
calibration_in_progress: bool
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def trim_outliers(points: list[SessionPoint]) -> list[SessionPoint]:
|
|
44
|
+
if len(points) < 20:
|
|
45
|
+
return points
|
|
46
|
+
|
|
47
|
+
import numpy as np
|
|
48
|
+
|
|
49
|
+
loads = np.array([point.composite_load for point in points], dtype=float)
|
|
50
|
+
low, high = np.percentile(loads, [5, 95])
|
|
51
|
+
filtered = [point for point in points if low <= point.composite_load <= high]
|
|
52
|
+
return filtered
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _tricube(distance: np.ndarray) -> np.ndarray:
|
|
56
|
+
import numpy as np
|
|
57
|
+
|
|
58
|
+
clipped = np.clip(1 - np.power(np.abs(distance), 3), 0, None)
|
|
59
|
+
return np.power(clipped, 3)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def lowess_smooth(xs: np.ndarray, ys: np.ndarray, bandwidth: float) -> np.ndarray:
|
|
63
|
+
import numpy as np
|
|
64
|
+
from scipy import linalg
|
|
65
|
+
|
|
66
|
+
n = len(xs)
|
|
67
|
+
if n == 0:
|
|
68
|
+
return np.array([])
|
|
69
|
+
if n == 1:
|
|
70
|
+
return ys.copy()
|
|
71
|
+
|
|
72
|
+
span = max(2, int(math.ceil(bandwidth * n)))
|
|
73
|
+
smoothed = np.zeros(n, dtype=float)
|
|
74
|
+
x_design = np.column_stack((np.ones(n), xs))
|
|
75
|
+
|
|
76
|
+
for index, x0 in enumerate(xs):
|
|
77
|
+
distances = np.abs(xs - x0)
|
|
78
|
+
h = np.partition(distances, min(span - 1, n - 1))[min(span - 1, n - 1)]
|
|
79
|
+
if h == 0:
|
|
80
|
+
weights = np.where(distances == 0, 1.0, 0.0)
|
|
81
|
+
else:
|
|
82
|
+
weights = _tricube(distances / h)
|
|
83
|
+
sqrt_w = np.sqrt(weights)
|
|
84
|
+
wx = x_design * sqrt_w[:, None]
|
|
85
|
+
wy = ys * sqrt_w
|
|
86
|
+
beta, *_ = linalg.lstsq(wx, wy)
|
|
87
|
+
smoothed[index] = beta[0] + beta[1] * x0
|
|
88
|
+
return smoothed
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def fit_babysitting_tax_curve(points: list[SessionPoint]) -> CurveResult:
|
|
92
|
+
import numpy as np
|
|
93
|
+
|
|
94
|
+
filtered = trim_outliers(points)
|
|
95
|
+
if len(filtered) < 10:
|
|
96
|
+
return CurveResult([], [], [], None, calibration_in_progress=True)
|
|
97
|
+
|
|
98
|
+
bandwidth = 0.5 if len(filtered) < 50 else 0.3
|
|
99
|
+
sorted_points = sorted(filtered, key=lambda point: point.n_agents_active)
|
|
100
|
+
xs = np.array([point.n_agents_active for point in sorted_points], dtype=float)
|
|
101
|
+
composite = np.array([point.composite_load for point in sorted_points], dtype=float)
|
|
102
|
+
quality_values = np.array(
|
|
103
|
+
[
|
|
104
|
+
point.output_quality if point.output_quality is not None else 0.0
|
|
105
|
+
for point in sorted_points
|
|
106
|
+
],
|
|
107
|
+
dtype=float,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
composite_curve = lowess_smooth(xs, composite, bandwidth)
|
|
111
|
+
quality_curve = lowess_smooth(xs, quality_values, bandwidth)
|
|
112
|
+
|
|
113
|
+
max_index = int(np.argmax(quality_curve))
|
|
114
|
+
optimal = int(round(xs[max_index])) if len(xs) else None
|
|
115
|
+
return CurveResult(
|
|
116
|
+
xs=xs.tolist(),
|
|
117
|
+
composite_curve=composite_curve.tolist(),
|
|
118
|
+
quality_curve=quality_curve.tolist(),
|
|
119
|
+
optimal_n_agents=optimal,
|
|
120
|
+
calibration_in_progress=False,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def render_curve_ascii(curve: CurveResult) -> str:
|
|
125
|
+
if curve.calibration_in_progress:
|
|
126
|
+
return "Calibration in progress: need at least 10 sessions."
|
|
127
|
+
if not curve.xs:
|
|
128
|
+
return "No curve data available."
|
|
129
|
+
|
|
130
|
+
import numpy as np
|
|
131
|
+
|
|
132
|
+
chars = "▁▂▃▄▅▆▇█"
|
|
133
|
+
values = np.array(curve.quality_curve, dtype=float)
|
|
134
|
+
if np.allclose(values.max(), values.min()):
|
|
135
|
+
sparkline = chars[0] * len(values)
|
|
136
|
+
else:
|
|
137
|
+
scaled = (values - values.min()) / (values.max() - values.min())
|
|
138
|
+
sparkline = "".join(chars[int(round(item * (len(chars) - 1)))] for item in scaled)
|
|
139
|
+
return f"Quality curve: {sparkline}"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def save_curve_png(curve: CurveResult, output_path: Path) -> None:
|
|
143
|
+
import matplotlib
|
|
144
|
+
|
|
145
|
+
matplotlib.use("Agg")
|
|
146
|
+
from matplotlib import pyplot as plt
|
|
147
|
+
|
|
148
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
149
|
+
plt.figure(figsize=(8, 4))
|
|
150
|
+
plt.plot(curve.xs, curve.composite_curve, label="Composite Load", color="#7a1f2f")
|
|
151
|
+
plt.plot(curve.xs, curve.quality_curve, label="Output Quality", color="#1f4f7a")
|
|
152
|
+
plt.xlabel("n_agents_active")
|
|
153
|
+
plt.ylabel("score")
|
|
154
|
+
plt.title("Babysitting Tax Curve")
|
|
155
|
+
plt.legend()
|
|
156
|
+
plt.tight_layout()
|
|
157
|
+
plt.savefig(output_path)
|
|
158
|
+
plt.close()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def weekly_summary(points: list[SessionPoint], *, now: datetime | None = None) -> dict[str, float]:
|
|
162
|
+
if not points:
|
|
163
|
+
return {"sessions": 0, "avg_composite_load": 0.0, "avg_quality": 0.0}
|
|
164
|
+
|
|
165
|
+
import numpy as np
|
|
166
|
+
|
|
167
|
+
now = now or datetime.now(tz=UTC)
|
|
168
|
+
week_ago = now - timedelta(days=7)
|
|
169
|
+
recent = [point for point in points if point.ended_at and point.ended_at >= week_ago]
|
|
170
|
+
if not recent:
|
|
171
|
+
return {"sessions": 0, "avg_composite_load": 0.0, "avg_quality": 0.0}
|
|
172
|
+
loads = [point.composite_load for point in recent]
|
|
173
|
+
quality = [point.output_quality or 0.0 for point in recent]
|
|
174
|
+
return {
|
|
175
|
+
"sessions": float(len(recent)),
|
|
176
|
+
"avg_composite_load": float(np.mean(loads)),
|
|
177
|
+
"avg_quality": float(np.mean(quality)),
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def render_weekly_table(summary: dict[str, float]) -> str:
|
|
182
|
+
from rich.console import Console
|
|
183
|
+
from rich.table import Table
|
|
184
|
+
|
|
185
|
+
console = Console(record=True)
|
|
186
|
+
table = Table(title="Weekly Babysitting Summary")
|
|
187
|
+
table.add_column("Metric")
|
|
188
|
+
table.add_column("Value")
|
|
189
|
+
table.add_row("Sessions", str(int(summary["sessions"])))
|
|
190
|
+
table.add_row("Avg Composite Load", f"{summary['avg_composite_load']:.2f}")
|
|
191
|
+
table.add_row("Avg Output Quality", f"{summary['avg_quality']:.2f}")
|
|
192
|
+
console.print(table)
|
|
193
|
+
return console.export_text()
|