@danypops/jittor 0.11.0 → 0.12.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 +21 -73
- package/package.json +13 -15
- package/src/cli-commands/service-daemon.ts +1 -1
- package/src/cli-commands/support.ts +1 -1
- package/src/cli.ts +1 -1
- package/src/client.ts +2 -2
- package/src/daemon.ts +8 -8
- package/src/db.ts +2 -2
- package/src/index.ts +137 -0
- package/src/log.ts +3 -3
- package/src/ports/session-identity-store.ts +2 -2
- package/src/service.ts +2 -2
- package/src/session-identity-service.ts +1 -1
- package/src/state.ts +10 -10
- package/src/version.ts +1 -1
- package/docs/USAGE_PRIOR_ART.md +0 -64
- package/extension/src/benchmark-tui.ts +0 -109
- package/extension/src/capabilities/codex-recovery.ts +0 -127
- package/extension/src/capabilities/http-headers.ts +0 -5
- package/extension/src/capabilities/local-run-telemetry.ts +0 -104
- package/extension/src/capabilities/provider-response-telemetry.ts +0 -96
- package/extension/src/footer.ts +0 -323
- package/extension/src/index.ts +0 -648
- package/extension/src/service-client.ts +0 -26
- package/extension/src/session-identity.ts +0 -20
- package/extension/src/settings-tui.ts +0 -153
- package/extension/src/settings.ts +0 -103
- package/extension/src/tui.ts +0 -279
- package/extension/src/usage.ts +0 -320
package/README.md
CHANGED
|
@@ -1,90 +1,41 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @danypops/jittor
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Jittor observes provider budgets and per-turn usage, computes whether the current burn rate is sustainable, and applies a deterministic policy before each model request:
|
|
6
|
-
|
|
7
|
-
1. continue
|
|
8
|
-
2. throttle
|
|
9
|
-
3. lower thinking
|
|
10
|
-
4. switch model
|
|
11
|
-
5. switch provider
|
|
12
|
-
6. halt
|
|
13
|
-
|
|
14
|
-
Initial telemetry providers:
|
|
15
|
-
|
|
16
|
-
- ChatGPT-authenticated Codex subscription usage
|
|
17
|
-
- OpenRouter API key usage, response accounting, and model pricing
|
|
18
|
-
- Anthropic official per-response rate-limit headers (requests, tokens, input/output tokens, and optional Priority Tier buckets)
|
|
19
|
-
- Google Vertex AI classified failure pressure (quota/auth/invalid-request/overload/transport), since Vertex has no documented remaining-budget header or personal polling endpoint
|
|
20
|
-
|
|
21
|
-
Jittor follows the Papyrus daemon architecture: a supervised Bun service owns SQLite and provider polling; the native Pi extension uses an authenticated loopback client and applies model/thinking decisions.
|
|
3
|
+
Supervised Bun daemon, router policy, provider telemetry adapters, and CLI for Jittor. See the [repo root README](../../README.md) for the two-package overview and [`@danypops/pi-jittor`](../pi-jittor) for the Pi extension that talks to this daemon.
|
|
22
4
|
|
|
23
5
|
## Architecture
|
|
24
6
|
|
|
25
|
-
The initial service scaffold is split into domain, ports, and adapters:
|
|
26
|
-
|
|
27
7
|
- `src/domain/metric.ts` — normalized timestamped metric observations
|
|
28
8
|
- `src/ports/metric-store.ts` — storage boundary used by the application service
|
|
29
9
|
- `src/adapters/sqlite-metric-store.ts` — SQLite time-series adapter
|
|
30
10
|
- `src/service.ts` — authenticated operation registry
|
|
31
11
|
- `src/client.ts` — operation-typed loopback client
|
|
32
12
|
- `src/daemon.ts` — Bun composition root and maintenance loop
|
|
13
|
+
- `src/index.ts` — the package's public surface: everything `@danypops/pi-jittor` (or any other consumer) imports
|
|
33
14
|
|
|
34
15
|
SQLite runs in WAL mode with versioned migrations, JSON validation, bounded queries, chronological indexes, pruning, and checkpoints. The database follows `XDG_DATA_HOME`; private authentication state follows `XDG_STATE_HOME`; the daemon handle follows `XDG_RUNTIME_DIR`.
|
|
35
16
|
|
|
36
17
|
Operations currently include bounded metric recording/query/pruning, benchmark refresh/status/query, context assessment, routing control, telemetry polling, and service checkpointing. Every operation is exposed through the authenticated typed client; benchmark operations also have CLI parity.
|
|
37
18
|
|
|
38
|
-
Provider
|
|
39
|
-
|
|
40
|
-
The third-party `anthropic-vertex` provider (Anthropic Claude models served through Google Vertex, e.g. via `@twogiants/pi-anthropic-vertex`) is tracked separately from both of the above: it reuses Pi's own Anthropic Messages stream implementation with Anthropic's official `@anthropic-ai/vertex-sdk` client, so its wire shape is Anthropic's, but its quota accounting is Google's. Jittor applies Google Vertex's failure classification to it (real-world reports confirm its 429s still carry GCP's own quota-exceeded shape even through Anthropic's own SDK) and, best-effort, also checks for genuine Anthropic rate-limit response headers on it, since it is unverified whether this specific passthrough ever forwards them. Either way, every metric is tagged `anthropic-vertex`, never blended into direct Anthropic's `anthropic` source or Pi's unrelated native `google-vertex` provider, since each represents a different account/quota pool. Its footer budget (labeled `vtok`/`vreq` when headers are observed) stays `null` (may still resolve) rather than `undefined` (provably impossible) until it's confirmed one way or the other.
|
|
41
|
-
|
|
42
|
-
The native Pi extension preflights input and every provider turn, applies model/thinking decisions, records response headers and finalized usage through the daemon, and blocks requests when required telemetry is unsafe. It follows Pi's current authenticated model/provider and synchronizes Pi's available models before every decision, so unavailable catalog routes are never selected. Mutable route state is scoped by Pi session, so concurrent sessions cannot replace each other's active provider or footer budget selection. Each session registers an opaque secret with the daemon at `session_start` (best-effort; a registration failure leaves that session unarmored rather than blocked) and presents it on every router-mutating call for the rest of its lifetime; an unregistered `session_id` continues to mutate exactly as before, so this is additive armor, not a breaking change for other callers of the same API. A configured required budget source still fails closed; a provider with no enforceable budget window continues explicitly monitor-only. Its responsive integrated footer groups repository and model identity with cumulative usage, a color-coded context-window bar, and current-provider budget telemetry. Codex shows the active model's bounded quota as a draining remaining-budget bar with reset and freshness information. OpenRouter uses the same drain semantics when its official key telemetry exposes a configured limit and remaining balance; keys without a limit remain honest text-only spend and never receive a fabricated denominator. Anthropic shows the same drain semantics from its most-restrictive-in-effect token bucket, falling back to the request bucket when no token telemetry has been observed yet. During Pi compaction, the context bar drains from its captured fill against a learned median duration estimated from the last few completed compactions (bounded to the most recent 20 samples, requiring at least 3 before trusting it). It never renders a timer. Until enough evidence exists, the bar does not drain; it blinks in place at its captured fill. Run `jittor compaction estimate [--json]` to inspect the current estimate and its confidence directly. Unknown and stale telemetry are marked explicitly. Run `/jittor` for the consolidated Settings TUI (its default action), or `/jittor status` for detailed burn pressure, freshness, route state, and confirmed emergency-halt/override controls.
|
|
43
|
-
|
|
44
|
-
Jittor currently registers no model-callable native tools, so Pi's native model `content` versus renderer `details` contract is explicitly not applicable. Daemon JSON, CLI `--json`, human CLI output, command notifications, panels, and the footer remain separate bounded channels. See [`docs/OUTPUT_CHANNELS.md`](docs/OUTPUT_CHANNELS.md) for the conformance matrix and the requirements that apply if a native tool is introduced later.
|
|
19
|
+
## Provider telemetry
|
|
45
20
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
Transient Codex recovery is securely off by default and controlled through the existing Jittor command surface:
|
|
51
|
-
|
|
52
|
-
```text
|
|
53
|
-
/jittor recovery status
|
|
54
|
-
/jittor recovery on
|
|
55
|
-
/jittor recovery off
|
|
56
|
-
/jittor recovery cancel
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
The on/off choice persists privately in `$XDG_CONFIG_HOME/jittor/extension.json` (or `~/.config/jittor/extension.json`). Status reports only enabled state, cooldown, bounded attempt/window counters, and the normalized failure class. `cancel` clears the current cooldown and attempt window without changing the persisted on/off choice.
|
|
60
|
-
|
|
61
|
-
Jittor observes finalized Codex assistant errors through Pi's public message lifecycle, classifies only bounded error metadata, and waits for `agent_settled` before acting. That boundary guarantees Pi's built-in retry, compaction retry, and queued follow-up work has finished. A transient concurrency, rate-limit, overload, or transport failure then schedules one hidden follow-up with Retry-After-aware capped jitter. Recovery is limited to three attempts per ten-minute window, never overlaps pending Pi messages, resets after success, and is canceled by human input or session shutdown. Quota, authentication, invalid-request, unknown, and aborted failures remain terminal. Raw provider payloads are never retained or injected.
|
|
62
|
-
|
|
63
|
-
### Settings
|
|
64
|
-
|
|
65
|
-
`/jittor` is the settings and control command. Bare `/jittor` (or `/jittor settings`) opens one keyboard-navigable TUI covering routing enforcement, the informational footer, Codex recovery, and all four token-budget thresholds, with explicit ON/OFF and configured/not-configured labels, bounded rendering on narrow terminals, and confirmation for weaker enforcement/recovery changes. `/jittor status` shows the routing/pressure panel that used to be the bare command's default. Existing non-TUI subcommands (`benchmarks`, `outcome`, `recovery`, `on`/`off`, `footer on`/`off`, `context`) remain available for automation and are unchanged.
|
|
66
|
-
|
|
67
|
-
### Usage and cost graphs
|
|
68
|
-
|
|
69
|
-
`/usage` is its own top-level command, separate from `/jittor`. Bare `/usage` opens a colored Unicode cumulative graph with X/Y axes, per-provider/model series, and explicit **Hourly**, **Daily**, **Weekly**, **Monthly**, and **Quarterly** periods; `/usage cost` opens the same graph showing aggregated USD spend instead of tokens, reusing the `cost` metric already recorded content-free on every finalized Pi assistant message (no new instrumentation). Left/Right or Tab/Shift+Tab changes the time frame, `v` toggles between the token and cost views, and `r` refreshes.
|
|
70
|
-
|
|
71
|
-
The graph fetches metrics per distinct provider/model scope (`jittor metrics distinct-scopes`, bounded to 40 scopes, 250 rows each) rather than one flat "most recent rows" query. A flat query lets one heavy, long-running session monopolize the entire row budget with its own most recent activity, silently hiding every other provider from the chart no matter which time frame is selected, since the query would never reach back far enough in time to see anything else. Fetching per scope guarantees every active provider/model gets its own fair share of the query budget instead.
|
|
72
|
-
|
|
73
|
-
### Cost per Papyrus task
|
|
21
|
+
- ChatGPT-authenticated Codex subscription usage
|
|
22
|
+
- OpenRouter API key usage, response accounting, and model pricing
|
|
23
|
+
- Anthropic official per-response rate-limit headers (requests, tokens, input/output tokens, and optional Priority Tier buckets)
|
|
24
|
+
- Google Vertex AI classified failure pressure (quota/auth/invalid-request/overload/transport), since Vertex has no documented remaining-budget header or personal polling endpoint
|
|
74
25
|
|
|
75
|
-
|
|
26
|
+
Provider adapters currently include official OpenRouter key/usage/model telemetry and an explicitly experimental Codex subscription adapter. The Codex adapter follows the pinned open-source CLI `/wham/usage` payload and `x-codex-*` response-header contracts, accepts additional metered limits, and fails closed on malformed windows or impossible percentages. File credentials must be explicitly configured and private (`0600`); Jittor reads only the access token and account ID, never refreshes credentials, and never logs or persists OAuth secrets. Anthropic has no personal-account polling endpoint (its Admin/Rate Limits API is documented as unavailable for individual accounts), so Jittor instead reads the official `anthropic-ratelimit-*` response headers Pi observes on every Messages API call and fails closed on schema drift the same way. Google Vertex AI has neither a personal polling endpoint nor a documented remaining-quota response header, so Jittor never fabricates a Vertex budget bar; it instead classifies Vertex's `google.rpc.Status` failure shape (quota, authentication, invalid-request, overload, transport, unknown) from Pi's bounded, content-free `errorMessage` and records only a bounded failure-count metric.
|
|
76
27
|
|
|
77
|
-
|
|
28
|
+
The third-party `anthropic-vertex` provider (Anthropic Claude models served through Google Vertex, e.g. via `@twogiants/pi-anthropic-vertex`) is tracked separately from both of the above: it reuses Pi's own Anthropic Messages stream implementation with Anthropic's official `@anthropic-ai/vertex-sdk` client, so its wire shape is Anthropic's, but its quota accounting is Google's. Jittor applies Google Vertex's failure classification to it (real-world reports confirm its 429s still carry GCP's own quota-exceeded shape even through Anthropic's own SDK) and, best-effort, also checks for genuine Anthropic rate-limit response headers on it, since it is unverified whether this specific passthrough ever forwards them. Either way, every metric is tagged `anthropic-vertex`, never blended into direct Anthropic's `anthropic` source or Pi's unrelated native `google-vertex` provider, since each represents a different account/quota pool.
|
|
78
29
|
|
|
79
|
-
|
|
30
|
+
Blocking always has a daemon-independent escape hatch: `/jittor off` (in the extension) immediately enters persisted monitor-only mode and never blocks provider requests, regardless of daemon state.
|
|
80
31
|
|
|
81
|
-
|
|
32
|
+
## Benchmark evidence
|
|
82
33
|
|
|
83
34
|
Jittor can ingest bounded OpenRouter model metadata, p50 latency/throughput ordering, and Design Arena Elo rankings as provenance-bearing evidence without treating OpenRouter as model-scope authority. Enable online ingestion explicitly with `JITTOR_OPENROUTER_BENCHMARKS=1`; it is off by default. OpenRouter model metadata and operational ordering are public; Design Arena ingestion additionally uses `OPENROUTER_API_KEY` from the supervised service environment without retaining it. Snapshots preserve the upstream publisher, normalized model identities, immutable retrieval revisions, source URLs, confidence, license terms, and explicit freshness deadlines. A malformed or oversized refresh leaves the last complete snapshot visible and records only a payload-safe failure state.
|
|
84
35
|
|
|
85
|
-
Design Arena rates models across dozens of arena/category pairs (music, video, text-to-speech, ASCII art, ...); Jittor ingests only the bounded allowlist of categories (`codecategories`, `website`, `uicomponent`, `dataviz`, `svg`) that measure frontend/UI-generation skill relevant to routing a coding agent, tagged into one `design` domain distinct from `coding`. A model with no OpenRouter-reachable identity (proprietary platforms, image/video generators) is skipped rather than fabricated into unroutable evidence.
|
|
36
|
+
Design Arena rates models across dozens of arena/category pairs (music, video, text-to-speech, ASCII art, ...); Jittor ingests only the bounded allowlist of categories (`codecategories`, `website`, `uicomponent`, `dataviz`, `svg`) that measure frontend/UI-generation skill relevant to routing a coding agent, tagged into one `design` domain distinct from `coding`. A model with no OpenRouter-reachable identity (proprietary platforms, image/video generators) is skipped rather than fabricated into unroutable evidence.
|
|
86
37
|
|
|
87
|
-
Jittor also ingests LMArena's own official Hugging Face dataset (`lmarena-ai/leaderboard-dataset`, via the public `datasets-server.huggingface.co` API -- no credential required) for its Code Arena (`webdev`) and Agent Arena human-preference battles, and, when `ARTIFICIAL_ANALYSIS_API_KEY` is configured, Artificial Analysis's own direct API (
|
|
38
|
+
Jittor also ingests LMArena's own official Hugging Face dataset (`lmarena-ai/leaderboard-dataset`, via the public `datasets-server.huggingface.co` API -- no credential required) for its Code Arena (`webdev`) and Agent Arena human-preference battles, and, when `ARTIFICIAL_ANALYSIS_API_KEY` is configured, Artificial Analysis's own direct API (adds a `math` domain and measured per-model latency). LMArena's Bradley-Terry/IPS ratings aren't on the same scale as Artificial Analysis's 0-100 indices, so they're tagged under distinct `-arena`-suffixed dimensions instead of blended into the same average.
|
|
88
39
|
|
|
89
40
|
Use the authenticated CLI channels independently:
|
|
90
41
|
|
|
@@ -96,17 +47,17 @@ jittor benchmarks list --source openrouter-models [--model provider/model] [--di
|
|
|
96
47
|
|
|
97
48
|
Only complete snapshots are queryable. Query output reports both completeness and freshness. See [`docs/BENCHMARK_SOURCES.md`](docs/BENCHMARK_SOURCES.md) for source authority, provenance, conflict, and redistribution rules.
|
|
98
49
|
|
|
99
|
-
|
|
50
|
+
The ranking operation (`domain/model-ranking.ts`) accepts an explicit bounded candidate set and never adds identities found only in evidence. It scores quality (a domain-specific dimension, e.g. `quality-coding`, and a type-specific dimension, e.g. `quality-type-planning`, each optional and additive over the universal `quality-general` fallback), cost, latency, context, and local reliability with bounded user weights, budget-pressure adjustment, component confidence, freshness, provenance, and deterministic tie-breaking. Missing evidence remains unknown and lowers confidence.
|
|
100
51
|
|
|
101
|
-
|
|
52
|
+
Jittor separately records content-free local model observations from Pi's public lifecycle: TTFT, wall latency, output throughput, token/cache/cost efficiency, provider retries, tool-loop counts, failures, and two independent classifications derived only from bounded tool names: domain (subject matter, e.g. `coding`) and type (activity, e.g. `research`, `planning`). Prompts, responses, tool arguments/results, credentials, and project paths are never retained. Robust aggregates report sample size, median, p90, median absolute deviation, recency, and confidence without merging local observations into external benchmark facts.
|
|
102
53
|
|
|
103
|
-
|
|
54
|
+
## Context pressure
|
|
104
55
|
|
|
105
56
|
Papyrus emits content-free prompt-injection observations through Pi's shared extension event bus. Jittor validates and records their exact Rule/Task character sizes, prompt share, fingerprint repetition, and explicitly estimated token size. Jittor also records completed, aborted, and unmatched Pi compactions with duration, reason, retry state, pre-compaction context usage, and bounded turns/injection/provider/cache usage since the previous compaction.
|
|
106
57
|
|
|
107
|
-
|
|
58
|
+
`jittor context [--since <epoch-ms>] [--until <epoch-ms>] [--json]` reports bounded average/p95/max injection, Rule/Task mix, unchanged rate, compaction frequency/duration/reasons, and between-compaction provider/cache facts. Repeated prompt content is not labeled billed waste: provider-reported input/cache usage and an injection-disabled control are required before making cost or compaction-causality claims.
|
|
108
59
|
|
|
109
|
-
|
|
60
|
+
## CLI operations
|
|
110
61
|
|
|
111
62
|
Every daemon operation is reachable from the CLI through the authenticated typed client only — no command reads the SQLite store or a provider adapter directly. Each command supports `--json` for stable machine output; without it, a purpose-built human presenter renders the same result, per [`docs/OUTPUT_CHANNELS.md`](docs/OUTPUT_CHANNELS.md).
|
|
112
63
|
|
|
@@ -114,10 +65,9 @@ Every daemon operation is reachable from the CLI through the authenticated typed
|
|
|
114
65
|
jittor metrics record --source <s> --scope <s> --metric <s> --value <number|null> --unit <unit> [--observed-at <ms>] [--attributes <json>] [--json]
|
|
115
66
|
jittor metrics record-batch --observations <json-array, max 100> [--json]
|
|
116
67
|
jittor metrics query [--source <s>] [--scope <s>] [--metric <s>] [--since <ms>] [--until <ms>] [--limit <n>] [--order asc|desc] [--json]
|
|
117
|
-
jittor metrics prune --before <ms> [--json]
|
|
68
|
+
jittor metrics prune --before <ms> [--force] [--json] # force required if before is newer than 24h ago
|
|
118
69
|
jittor metrics distinct-scopes --source <s> --since <ms> --until <ms> [--limit 1..40] [--json]
|
|
119
70
|
jittor metrics cost-by-task --since <ms> --until <ms> [--json]
|
|
120
|
-
jittor metrics prune --before <ms> [--force] [--json] # force required if before is newer than 24h ago
|
|
121
71
|
jittor service checkpoint [--json]
|
|
122
72
|
jittor telemetry poll [--json]
|
|
123
73
|
jittor compaction estimate [--json]
|
|
@@ -132,7 +82,7 @@ jittor op <operation> [--input <json>]
|
|
|
132
82
|
|
|
133
83
|
`jittor op` is a raw escape hatch restricted to the daemon's own `EXPECTED_OPERATION_NAMES`; it rejects an unrecognized operation name before ever reaching the daemon rather than forwarding it blindly. Human-readable metric listings and router status are bounded (at most 50 metric rows and 20 telemetry sources are printed; `--json` output is bounded independently by the daemon's own query and response-size limits). No command prints the daemon bearer token, a provider API key, or an OAuth credential; a daemon-unavailable error stays actionable ("install or start jittor.service") without ever including the token used to reach it.
|
|
134
84
|
|
|
135
|
-
See [`docs/CALIBRATION.md`](docs/CALIBRATION.md) for thresholds and rollback, and [`docs/
|
|
85
|
+
See [`docs/CALIBRATION.md`](docs/CALIBRATION.md) for routing thresholds and rollback, and [`docs/PROVIDER_RESEARCH.md`](docs/PROVIDER_RESEARCH.md) for verified provider API boundaries and caveats.
|
|
136
86
|
|
|
137
87
|
```bash
|
|
138
88
|
bun test
|
|
@@ -141,5 +91,3 @@ bun run service:install
|
|
|
141
91
|
```
|
|
142
92
|
|
|
143
93
|
The systemd user unit binds only to `127.0.0.1`, discovers a 256-bit token without logging it, restarts on failure, and exposes authenticated health and operation endpoints.
|
|
144
|
-
|
|
145
|
-
See [`docs/PROVIDER_RESEARCH.md`](docs/PROVIDER_RESEARCH.md) for verified API boundaries and caveats.
|
package/package.json
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/jittor",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Just-in-Time Token Optimizing Router for Pi",
|
|
3
|
+
"version": "0.12.1",
|
|
4
|
+
"description": "Just-in-Time Token Optimizing Router for Pi -- supervised daemon, router policy, and CLI",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"main": "./src/index.ts",
|
|
7
|
+
"types": "./src/index.ts",
|
|
6
8
|
"keywords": ["pi-package", "llm-router", "token-budget"],
|
|
7
9
|
"bin": {
|
|
8
10
|
"jittor": "src/cli.ts"
|
|
@@ -11,19 +13,11 @@
|
|
|
11
13
|
"test": "bun test",
|
|
12
14
|
"typecheck": "tsc --noEmit",
|
|
13
15
|
"serve": "bun src/cli.ts serve",
|
|
14
|
-
"service:install": "bun src/cli.ts service install"
|
|
15
|
-
"guard:install": "git config core.hooksPath .githooks"
|
|
16
|
-
},
|
|
17
|
-
"pi": {
|
|
18
|
-
"extensions": ["extension/src/index.ts"]
|
|
19
|
-
},
|
|
20
|
-
"peerDependencies": {
|
|
21
|
-
"@earendil-works/pi-coding-agent": "*",
|
|
22
|
-
"@earendil-works/pi-tui": "*",
|
|
23
|
-
"typebox": "*"
|
|
16
|
+
"service:install": "bun src/cli.ts service install"
|
|
24
17
|
},
|
|
25
18
|
"dependencies": {
|
|
26
|
-
"@danypops/
|
|
19
|
+
"@danypops/vehicle-server": "^0.2.1",
|
|
20
|
+
"@danypops/vehicle-client": "^0.1.1",
|
|
27
21
|
"google-auth-library": "^10.9.0"
|
|
28
22
|
},
|
|
29
23
|
"devDependencies": {
|
|
@@ -31,7 +25,11 @@
|
|
|
31
25
|
},
|
|
32
26
|
"repository": {
|
|
33
27
|
"type": "git",
|
|
34
|
-
"url": "git+https://github.com/DanyPops/jittor.git"
|
|
28
|
+
"url": "git+https://github.com/DanyPops/jittor.git",
|
|
29
|
+
"directory": "packages/jittor"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
35
33
|
},
|
|
36
|
-
"files": ["src", "
|
|
34
|
+
"files": ["src", "docs", "README.md"]
|
|
37
35
|
}
|
|
@@ -41,7 +41,7 @@ export function systemctl(...args: string[]): void {
|
|
|
41
41
|
|
|
42
42
|
/** cliPath is the caller's own entrypoint file -- resolved from the real CLI script's `import.meta.url`, never this module's own, so the installed unit's ExecStart always points at the actual runnable CLI. */
|
|
43
43
|
export function installService(cliPath: string): void {
|
|
44
|
-
const unitPath = resolveJittorPaths().
|
|
44
|
+
const unitPath = resolveJittorPaths().serviceDescriptor;
|
|
45
45
|
mkdirSync(dirname(unitPath), { recursive: true });
|
|
46
46
|
const codexAuthFile = join(process.env["CODEX_HOME"] ?? join(homedir(), ".codex"), "auth.json");
|
|
47
47
|
writeFileSync(unitPath, renderSystemdUnit({
|
package/src/cli.ts
CHANGED
|
@@ -52,7 +52,7 @@ function usage(stderr: (line: string) => void): number {
|
|
|
52
52
|
export async function runCli(args: string[], deps: CliDependencies = DEFAULT_DEPENDENCIES): Promise<number> {
|
|
53
53
|
const [command, action, ...rest] = args;
|
|
54
54
|
const fail = () => usage(deps.stderr);
|
|
55
|
-
if (command === "serve") { deps.serve(); return 0; }
|
|
55
|
+
if (command === "serve") { await deps.serve(); return 0; }
|
|
56
56
|
if (command === "session") return runSessionCommand(action, rest, deps, fail);
|
|
57
57
|
if (command === "metrics") return runMetricsCommand(action, rest, deps, fail);
|
|
58
58
|
if (command === "telemetry") return runTelemetryCommand(action, rest, deps, fail);
|
package/src/client.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthenticatedRpcClient, type FetchTransport } from "@danypops/
|
|
1
|
+
import { AuthenticatedRpcClient, type FetchTransport } from "@danypops/vehicle-client/rpc-client";
|
|
2
2
|
import type { OperationInputs, OperationName, OperationOutputs } from "./service.ts";
|
|
3
3
|
import { ensureAuthToken, readDaemonHandle, resolveJittorPaths, type JittorPaths } from "./state.ts";
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@ export type { FetchTransport };
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Jittor's typed authenticated RPC client, now a thin named subclass of
|
|
9
|
-
* `@danypops/
|
|
9
|
+
* `@danypops/vehicle-client/rpc-client`'s `AuthenticatedRpcClient` -- the shared substrate factored
|
|
10
10
|
* out after jittor's own client.ts and web-spider-daemon's were found byte-identical (see
|
|
11
11
|
* daemon-kit's README). Keeps the old 3-positional-argument constructor so every existing call
|
|
12
12
|
* site is untouched by this migration.
|
package/src/daemon.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { startDaemon as startDaemonKit, type RunningDaemon } from "@danypops/
|
|
1
|
+
import { startDaemon as startDaemonKit, type RunningDaemon } from "@danypops/vehicle-server/daemon";
|
|
2
2
|
import { MAINTENANCE_INTERVAL_MS, TELEMETRY_POLL_INTERVAL_MS } from "./constants.ts";
|
|
3
3
|
import { DEFAULT_POLICY, UNCONFIGURED_ROUTE } from "./config.ts";
|
|
4
4
|
import { SQLiteMetricStore } from "./adapters/sqlite-metric-store.ts";
|
|
@@ -23,7 +23,7 @@ import type { GoogleVertexMetricSource } from "./providers/google-vertex-contrac
|
|
|
23
23
|
import { ensureAuthToken, resolveJittorPaths, type JittorPaths } from "./state.ts";
|
|
24
24
|
import { logEvent, logger } from "./log.ts";
|
|
25
25
|
|
|
26
|
-
export type { RunningDaemon } from "@danypops/
|
|
26
|
+
export type { RunningDaemon } from "@danypops/vehicle-server/daemon";
|
|
27
27
|
|
|
28
28
|
export function reportMaintenanceFailure(event: string, error: unknown): void {
|
|
29
29
|
logEvent("error", event, { message: error instanceof Error ? error.message : String(error) });
|
|
@@ -65,7 +65,7 @@ export function telemetrySourcesFromEnvironment(env: Record<string, string | und
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
|
-
* Composition root, now built on `@danypops/
|
|
68
|
+
* Composition root, now built on `@danypops/vehicle-server/daemon`'s `startDaemon` for binding,
|
|
69
69
|
* atomic handle write, maintenance-timer driving, and clean shutdown -- the skeleton that used to
|
|
70
70
|
* be hand-rolled here (and, byte-identically, in web-spider-daemon's and papyrus's daemon.ts; see
|
|
71
71
|
* daemon-kit's README). Each maintenance task still catches and classifies its own failure via
|
|
@@ -74,10 +74,10 @@ export function telemetrySourcesFromEnvironment(env: Record<string, string | und
|
|
|
74
74
|
* daemon-kit's own generic "maintenance task failed: <name>" catch, which exists as a safety net
|
|
75
75
|
* for tasks that don't self-classify, not to replace a consumer's own richer classification.
|
|
76
76
|
*/
|
|
77
|
-
export function startDaemon(
|
|
77
|
+
export async function startDaemon(
|
|
78
78
|
paths: JittorPaths = resolveJittorPaths(),
|
|
79
79
|
env: Record<string, string | undefined> = process.env,
|
|
80
|
-
): RunningDaemon {
|
|
80
|
+
): Promise<RunningDaemon> {
|
|
81
81
|
const token = ensureAuthToken(paths);
|
|
82
82
|
const db = openJittorDb(paths.database);
|
|
83
83
|
const metrics = new SQLiteMetricStore(db);
|
|
@@ -96,7 +96,7 @@ export function startDaemon(
|
|
|
96
96
|
});
|
|
97
97
|
const service = new JittorService(metrics, router, benchmarks, modelRanker, sessionIdentity);
|
|
98
98
|
|
|
99
|
-
const daemon = startDaemonKit({
|
|
99
|
+
const daemon = await startDaemonKit({
|
|
100
100
|
daemonLabel: "Jittor",
|
|
101
101
|
handlePath: paths.handle,
|
|
102
102
|
logger,
|
|
@@ -115,8 +115,8 @@ export function startDaemon(
|
|
|
115
115
|
return daemon;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
export function serveMain(): void {
|
|
119
|
-
const daemon = startDaemon();
|
|
118
|
+
export async function serveMain(): Promise<void> {
|
|
119
|
+
const daemon = await startDaemon();
|
|
120
120
|
console.error(`[jittor] listening on ${daemon.host}:${daemon.port}`);
|
|
121
121
|
const stop = async (): Promise<void> => {
|
|
122
122
|
await daemon.stop();
|
package/src/db.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Database } from "bun:sqlite";
|
|
2
|
-
import { openSqliteWithPragmas } from "@danypops/
|
|
2
|
+
import { openSqliteWithPragmas } from "@danypops/vehicle-server/storage";
|
|
3
3
|
import { SQLITE_BUSY_TIMEOUT_MS } from "./constants.ts";
|
|
4
4
|
|
|
5
5
|
const INITIAL_SCHEMA = `
|
|
@@ -31,7 +31,7 @@ CREATE INDEX session_identities_last_seen_idx
|
|
|
31
31
|
`;
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* Delegates bootstrap (pragmas, migration engine) to `@danypops/
|
|
34
|
+
* Delegates bootstrap (pragmas, migration engine) to `@danypops/vehicle-server/storage`, which
|
|
35
35
|
* generalizes the byte-identical pragma/PRAGMA-user_version skeleton jittor's own db.ts used to
|
|
36
36
|
* hand-roll (see daemon-kit's README). Jittor's only remaining responsibility is its own schema.
|
|
37
37
|
*/
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
export * from "./constants.ts";
|
|
2
|
+
export {
|
|
3
|
+
type FetchTransport,
|
|
4
|
+
JittorClient,
|
|
5
|
+
connectJittorClient,
|
|
6
|
+
} from "./client.ts";
|
|
7
|
+
export {
|
|
8
|
+
EXPECTED_OPERATION_NAMES,
|
|
9
|
+
InvalidSessionSecretError,
|
|
10
|
+
JittorService,
|
|
11
|
+
UnknownOperationError,
|
|
12
|
+
createApp,
|
|
13
|
+
type JittorAppOptions,
|
|
14
|
+
type OperationInputs,
|
|
15
|
+
type OperationName,
|
|
16
|
+
type OperationOutputs,
|
|
17
|
+
} from "./service.ts";
|
|
18
|
+
export {
|
|
19
|
+
type CompactionDurationEstimate,
|
|
20
|
+
type CompactionStart,
|
|
21
|
+
CompactionTelemetry,
|
|
22
|
+
type ContextAssessment,
|
|
23
|
+
type PapyrusContextInjection,
|
|
24
|
+
assessContextTelemetry,
|
|
25
|
+
estimateCompactionDuration,
|
|
26
|
+
papyrusContextMetric,
|
|
27
|
+
validatePapyrusContextInjection,
|
|
28
|
+
} from "./domain/context-telemetry.ts";
|
|
29
|
+
export {
|
|
30
|
+
type TaskFocusEvent,
|
|
31
|
+
type TaskFocusStatus,
|
|
32
|
+
applyTaskFocusEvent,
|
|
33
|
+
validateTaskFocusEvent,
|
|
34
|
+
} from "./domain/task-focus.ts";
|
|
35
|
+
export {
|
|
36
|
+
METRIC_UNITS,
|
|
37
|
+
type MetricObservation,
|
|
38
|
+
type MetricQuery,
|
|
39
|
+
type MetricUnit,
|
|
40
|
+
type StoredMetricObservation,
|
|
41
|
+
validateMetricObservation,
|
|
42
|
+
} from "./domain/metric.ts";
|
|
43
|
+
export {
|
|
44
|
+
TASK_DOMAINS,
|
|
45
|
+
TASK_TYPES,
|
|
46
|
+
type ExplicitOutcome,
|
|
47
|
+
type ModelAggregateOptions,
|
|
48
|
+
type ModelMetricAggregate,
|
|
49
|
+
type ModelRunObservation,
|
|
50
|
+
type ModelTaskClassification,
|
|
51
|
+
type ModelTaskDomain,
|
|
52
|
+
type ModelTaskType,
|
|
53
|
+
aggregateModelMetrics,
|
|
54
|
+
classifyTaskFromTools,
|
|
55
|
+
modelRunMetrics,
|
|
56
|
+
validateModelRunObservation,
|
|
57
|
+
} from "./domain/model-observation.ts";
|
|
58
|
+
export {
|
|
59
|
+
type ModelCandidate,
|
|
60
|
+
type ModelRankingInput,
|
|
61
|
+
type ModelRankingResult,
|
|
62
|
+
type RankedModel,
|
|
63
|
+
type RankingProvenance,
|
|
64
|
+
type ScopeAuthority,
|
|
65
|
+
type UtilityComponent,
|
|
66
|
+
type UtilityComponentName,
|
|
67
|
+
type UtilityWeights,
|
|
68
|
+
rankModelCandidates,
|
|
69
|
+
} from "./domain/model-ranking.ts";
|
|
70
|
+
export {
|
|
71
|
+
USAGE_PERIODS,
|
|
72
|
+
type CostBucket,
|
|
73
|
+
type CostGraph,
|
|
74
|
+
type CostSeries,
|
|
75
|
+
type UsageAggregateRow,
|
|
76
|
+
type UsageBreakdown,
|
|
77
|
+
type UsageBucket,
|
|
78
|
+
type UsageBucketWindow,
|
|
79
|
+
type UsageGraph,
|
|
80
|
+
type UsageGraphOptions,
|
|
81
|
+
type UsagePeriod,
|
|
82
|
+
type UsageSeries,
|
|
83
|
+
buildCostGraph,
|
|
84
|
+
buildUsageGraph,
|
|
85
|
+
identity,
|
|
86
|
+
resolveUsageWindow,
|
|
87
|
+
usageBucketIndex,
|
|
88
|
+
usagePeriod,
|
|
89
|
+
usagePeriodStart,
|
|
90
|
+
} from "./domain/usage.ts";
|
|
91
|
+
export {
|
|
92
|
+
CodexRecoveryPolicy,
|
|
93
|
+
classifyCodexFailure,
|
|
94
|
+
type CodexFailure,
|
|
95
|
+
type CodexFailureKind,
|
|
96
|
+
type CodexFailureMetadata,
|
|
97
|
+
type CodexRecoveryAttempt,
|
|
98
|
+
type CodexRecoveryOptions,
|
|
99
|
+
type CodexRecoveryPlan,
|
|
100
|
+
} from "./domain/codex-recovery.ts";
|
|
101
|
+
export {
|
|
102
|
+
hasAnthropicRateLimitHeaders,
|
|
103
|
+
parseAnthropicRateLimitHeaders,
|
|
104
|
+
type AnthropicMetricSource,
|
|
105
|
+
type AnthropicRateLimitSnapshot,
|
|
106
|
+
type AnthropicRateLimitWindow,
|
|
107
|
+
} from "./providers/anthropic-contracts.ts";
|
|
108
|
+
export { parseCodexRateLimitHeaders } from "./providers/codex.ts";
|
|
109
|
+
export {
|
|
110
|
+
classifyGoogleVertexFailure,
|
|
111
|
+
googleVertexFailureMetrics,
|
|
112
|
+
type GoogleVertexFailure,
|
|
113
|
+
type GoogleVertexFailureKind,
|
|
114
|
+
type GoogleVertexFailureMetadata,
|
|
115
|
+
type GoogleVertexMetricSource,
|
|
116
|
+
} from "./providers/google-vertex-contracts.ts";
|
|
117
|
+
export {
|
|
118
|
+
evaluateRoutingPolicy,
|
|
119
|
+
type BudgetWindow,
|
|
120
|
+
type PolicyAction,
|
|
121
|
+
type PolicyConfig,
|
|
122
|
+
type PolicyDecision,
|
|
123
|
+
type PolicyInput,
|
|
124
|
+
type PolicyThresholds,
|
|
125
|
+
type PreviousDecision,
|
|
126
|
+
type Route,
|
|
127
|
+
type TelemetryFreshness,
|
|
128
|
+
} from "./policy.ts";
|
|
129
|
+
export type {
|
|
130
|
+
RouteOverride,
|
|
131
|
+
RouterController,
|
|
132
|
+
RouterStatus,
|
|
133
|
+
TelemetryPollResult,
|
|
134
|
+
TelemetrySourceStatus,
|
|
135
|
+
} from "./ports/router-controller.ts";
|
|
136
|
+
export type { DistinctScopesFilter, MetricStore, UsageAggregateFilter } from "./ports/metric-store.ts";
|
|
137
|
+
export { VERSION as jittorVersion } from "./version.ts";
|
package/src/log.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Structured daemon logging, now backed by `@danypops/
|
|
2
|
+
* Structured daemon logging, now backed by `@danypops/vehicle-server/logging` (pino) instead of a
|
|
3
3
|
* hand-rolled `console.error(JSON.stringify(...))` -- daemon-kit's own module doc explains why:
|
|
4
4
|
* level ordering/filtering/child-scoping is exactly the kind of thing worth one shared,
|
|
5
5
|
* dependency-backed implementation instead of four independent hand-rolled ones. One deliberate,
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* four daemons. `component`/`level`/`timestamp` and credential-safety (callers still must pass
|
|
9
9
|
* only bounded, non-sensitive fields) are unchanged.
|
|
10
10
|
*/
|
|
11
|
-
import { createLogger, type LogLevel as
|
|
11
|
+
import { createLogger, type LogLevel as VehicleLogLevel, type Logger } from "@danypops/vehicle-server/logging";
|
|
12
12
|
|
|
13
|
-
export type LogLevel = Extract<
|
|
13
|
+
export type LogLevel = Extract<VehicleLogLevel, "info" | "warn" | "error">;
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Also passed directly as `StartDaemonOptions.logger` so daemon-kit's own maintenance-task
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { SessionIdentityRecord, SessionIdentityStore as
|
|
1
|
+
import type { SessionIdentityRecord, SessionIdentityStore as VehicleSessionIdentityStore } from "@danypops/vehicle-server/session-identity";
|
|
2
2
|
|
|
3
3
|
/** Jittor's persistence port for daemon-kit's storage-agnostic session-identity primitive. */
|
|
4
|
-
export type SessionIdentityStore =
|
|
4
|
+
export type SessionIdentityStore = VehicleSessionIdentityStore;
|
|
5
5
|
export type { SessionIdentityRecord };
|
package/src/service.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { errorResponse, healthResponse, readyResponse, requireBearerToken } from "@danypops/
|
|
1
|
+
import { errorResponse, healthResponse, readyResponse, requireBearerToken } from "@danypops/vehicle-server/rpc-http";
|
|
2
2
|
import { SERVICE_MAX_BODY_BYTES, SERVICE_MAX_RESPONSE_BYTES } from "./constants.ts";
|
|
3
3
|
import { InvalidSessionSecretError, SessionIdentity, type RegisterSessionIdentityResult } from "./session-identity-service.ts";
|
|
4
4
|
import { VERSION } from "./version.ts";
|
|
@@ -188,7 +188,7 @@ export interface JittorAppOptions {
|
|
|
188
188
|
|
|
189
189
|
/**
|
|
190
190
|
* Bearer-check and the trivial health/ready/not-found responses now delegate to
|
|
191
|
-
* `@danypops/
|
|
191
|
+
* `@danypops/vehicle-server/rpc-http` (the same handful of lines every daemon's service.ts hand-rolled).
|
|
192
192
|
* The response-size guard below stays jittor-specific: daemon-kit's `jsonResponse` is intentionally
|
|
193
193
|
* unbounded (it has no operation dispatch of its own to guard), while jittor's `/api/v1/ops` can
|
|
194
194
|
* return arbitrarily large query results that must be capped (see SERVICE_MAX_RESPONSE_BYTES).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isSessionRegistered, registerSessionIdentity, releaseSessionIdentity, verifySessionSecret } from "@danypops/
|
|
1
|
+
import { isSessionRegistered, registerSessionIdentity, releaseSessionIdentity, verifySessionSecret } from "@danypops/vehicle-server/session-identity";
|
|
2
2
|
import type { SessionIdentityStore } from "./ports/session-identity-store.ts";
|
|
3
3
|
|
|
4
4
|
export interface RegisterSessionIdentityResult {
|
package/src/state.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Jittor's XDG paths/token/handle layout, now delegating to `@danypops/
|
|
2
|
+
* Jittor's XDG paths/token/handle layout, now delegating to `@danypops/vehicle-server/paths` --
|
|
3
3
|
* the shared substrate factored out after jittor's own state.ts and web-spider-daemon's were
|
|
4
4
|
* found byte-identical (see daemon-kit's README). Kept as a thin jittor-named wrapper (same
|
|
5
5
|
* exported function names/signatures as before) so every existing call site (daemon.ts,
|
|
6
6
|
* client.ts, cli.ts, and their tests) is untouched by this migration.
|
|
7
7
|
*/
|
|
8
8
|
import {
|
|
9
|
-
ensureAuthToken as
|
|
10
|
-
readDaemonHandle as
|
|
11
|
-
removeDaemonHandle as
|
|
9
|
+
ensureAuthToken as ensureVehicleAuthToken,
|
|
10
|
+
readDaemonHandle as readVehicleHandle,
|
|
11
|
+
removeDaemonHandle as removeVehicleHandle,
|
|
12
12
|
resolveDaemonPaths,
|
|
13
|
-
writeDaemonHandle as
|
|
13
|
+
writeDaemonHandle as writeVehicleHandle,
|
|
14
14
|
type DaemonHandle,
|
|
15
15
|
type DaemonPaths,
|
|
16
16
|
type PathEnvironment,
|
|
17
|
-
} from "@danypops/
|
|
17
|
+
} from "@danypops/vehicle-server/paths";
|
|
18
18
|
import {
|
|
19
19
|
DATABASE_FILENAME,
|
|
20
20
|
HANDLE_FILENAME,
|
|
@@ -39,17 +39,17 @@ export function resolveJittorPaths(options: PathEnvironment = {}): JittorPaths {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export function ensureAuthToken(paths: JittorPaths = resolveJittorPaths()): string {
|
|
42
|
-
return
|
|
42
|
+
return ensureVehicleAuthToken(paths.token, "Jittor");
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export function writeDaemonHandle(paths: JittorPaths, handle: DaemonHandle): void {
|
|
46
|
-
|
|
46
|
+
writeVehicleHandle(paths.handle, handle);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export function readDaemonHandle(paths: JittorPaths = resolveJittorPaths()): DaemonHandle | null {
|
|
50
|
-
return
|
|
50
|
+
return readVehicleHandle(paths.handle);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export function removeDaemonHandle(paths: JittorPaths = resolveJittorPaths()): void {
|
|
54
|
-
|
|
54
|
+
removeVehicleHandle(paths.handle);
|
|
55
55
|
}
|
package/src/version.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readPackageVersion } from "@danypops/
|
|
1
|
+
import { readPackageVersion } from "@danypops/vehicle-server/version";
|
|
2
2
|
|
|
3
3
|
/** Runtime package version; package.json is the single release source of truth. */
|
|
4
4
|
export const VERSION = readPackageVersion(new URL("../package.json", import.meta.url), "Jittor");
|