Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
332 changes: 38 additions & 294 deletions README.md

Large diffs are not rendered by default.

42 changes: 42 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Configuration

[Documentation index](../README.md#documentation)

## CLI configuration

Precedence: **changed flags > nonblank environment > TOML > defaults**.

| Setting | Flag | Environment | Default |
| --- | --- | --- | --- |
| API key | `--api-key` | `TS_JEV_API_KEY`, then `TYPESAFE_API_KEY` | Required for Jev |
| Base URL | `--base-url` | `TS_JEV_BASE_URL`, then `TYPESAFE_BASE_URL` | `https://api.typesafe.ai` |
| Model | `--model` | `TS_JEV_MODEL`, then `TYPESAFE_DEFAULT_MODEL` | `jev-latest` |
| Provider | `--provider` | `TS_JEV_PROVIDER` | `jev` |
| Operation timeout | `--timeout` | `TS_JEV_TIMEOUT` | `10s` |

- Explicit file: `--config /path/to/config.toml`. Missing or invalid explicit files fail.
- Otherwise: current-directory `jevwise.toml`, then `$XDG_CONFIG_HOME/ts-jev/config.toml` or `$HOME/.config/ts-jev/config.toml`. Only a missing global file is allowed. The selected file replaces rather than merges with global settings; no ancestor-directory search.
- On Windows, default config discovery uses `%USERPROFILE%\.config\ts-jev\config.toml` when `XDG_CONFIG_HOME` is unset.
- Trust the current directory before running decision/MCP commands: local settings can change the upstream destination. Environment/changed flags still win. Invalid, oversized (over 1 MiB), linked or nonregular local files fail without global fallback; help/version do not load them.
- Start with [config.example.toml](../config.example.toml). Prefer environment credentials rather than command-line secrets.
- TOML `api_key` supports `$NAME`/`${NAME}`, for example `api_key = "${MY_JEV_KEY}"`. Runtime loading expands references and trims the result; undefined variables become empty.
- Environment and `--api-key` values stay literal. File viewing never expands references or requires a key; Jev service initialization requires a nonblank key.

### Initialize, inspect and edit

```sh
jevwise config init # Global XDG/HOME config, even if local exists
jevwise config init --local # ./jevwise.toml, automatically selected in this cwd
jevwise config view # Selected file/defaults, API key redacted
jevwise config view --config /path/to/config.toml
jevwise config edit # Selected local/global file, vim by default
jevwise config edit --editor nvim # One-shot override, also for repairs
```

- Init never overwrites existing files, directories or links. It writes a template, not your environment credentials; Unix file permissions are requested as `0600`, new parent directories as `0700`. Use a trusted parent with safe ownership/ACLs: mode bits alone do not guarantee privacy, including inherited Unix ACLs. Windows users must manage ACLs separately.
- `--config PATH` also selects an init destination; do not combine it with `--local` or pass a blank path. View ignores environment/decision flag overrides, unknown keys and comments; invalid files fail safely. It is not a dump of effective runtime settings. Only the `api_key` field is automatically redacted; do not store credentials in other displayed settings.
- Set `editor = "nvim"` (or another executable name/path) in TOML to change the editor. Selection is `config edit --editor` > selected file's `editor` > `vim` on PATH; `$EDITOR`/`$VISUAL` are not read. No editor is bundled and no shell is used. Arguments are not parsed: use a trusted wrapper executable if options such as `--wait` are needed.
- Edit forwards terminal input/output and waits for the direct editor process. It uses the command context, not the decision timeout; missing editors/nonzero exits fail safely. Invalid TOML falls back to Vim for repair; wrong-typed editor settings or oversized files can be repaired using `--editor`. It refuses missing files, directories and final symlinks.
- Go-copy pipe waits are bounded after process exit/cancellation. Only the direct editor process is managed, not wrapper descendants; arbitrary blocking custom readers/writers cannot be forcibly interrupted.
- Trust local editor settings and wrappers before editing: they select a program to execute. Editor terminal output intentionally may show the file you are editing. macOS/Linux support assumes an installed editor; no Windows editor availability/behavior is promised.
- Add `jevwise.toml` to your project's ignore rules before storing credentials. Prefer environment API keys.
16 changes: 16 additions & 0 deletions docs/debug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Debugging

[Documentation index](../README.md#documentation)

## Debug logging

```sh
jevwise --debug update --check
jevwise --debug mcp
```

- Debug logging is opt-in via `--debug`, uses `github.com/phuslu/log`, and writes JSON to stderr. Normal stdout stays unchanged; logs can be redirected with `2>debug.log`.
- Covers command/stage outcomes and timing, config loading, API attempts/retries/status codes, release verification/replacement, skills, MCP requests/lifecycle and authenticated management.
- Never logs credentials, prompts/options/results, user-supplied URLs/paths, raw headers/bodies, or raw error causes. Release checks may log their fixed public GitHub endpoint and strictly numeric rate-limit metadata. Treat timing/status diagnostics as operationally sensitive when sharing logs.
- Detached background mode traces the launcher only; child output remains isolated. There is no persistent debug log file. Use foreground MCP for request diagnostics, then restart without `--debug` when finished.
- Logging is best-effort. Write failures do not change operation results. No global logger or SDK configuration is changed.
48 changes: 48 additions & 0 deletions docs/development.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Development

[Documentation index](../README.md#documentation)

## Build from source

Use Go 1.27.2 or newer, matching `go.mod`. Run these commands from the checkout:

```sh
git clone https://github.com/bitbrew-dev/jevwise.git
cd jevwise
go build -o bin/jevwise ./cmd
bin/jevwise --help
bin/jevwise decide --help
```

Help does not read credentials, configuration files, or contact the API.

The current checkout uses `jevwise`; releases through v1.2.0 used `jev`. There is no `jev` alias. Existing configuration and the `jev` provider remain unchanged. Verify ownership before removing an older binary; another project may own the `jev` command.

## Version and platform builds

```sh
bin/jevwise version
bin/jevwise --version
make build-platform GOOS=windows GOARCH=amd64
make build-platform GOOS=windows GOARCH=arm64
```

- Both version forms report version, commit, and build date without credentials, config loading, or network requests.
- Plain `go build` reports `dev` with unknown metadata. Make injects metadata; only a clean exact tag is automatically a release version, otherwise `dev`.
- Release linker symbols are `github.com/bitbrew-dev/jevwise/internal/buildinfo.Version`, `.Commit`, and `.Date`.
- Make produces `build/jevwise-windows-amd64.exe` or `build/jevwise-windows-arm64.exe`. Make helpers require Unix shell tools; native Windows PowerShell can use `go build -o jevwise.exe ./cmd`, then `.\jevwise.exe --version`.

## Tests

```sh
go mod tidy -diff
go test -mod=readonly -count=1 ./...
go test -mod=readonly -race -count=1 ./...
go vet -mod=readonly ./...
go build -mod=readonly ./...
```

Tests use injected services/transports and local HTTP fixtures, not paid API requests.

- Compatibility is pinned to Python SDK v0.7.2, revision `f078f1e208a0d885154dc758344ae4fce77ac168`.
- Release/update QA uses injected HTTP responses and temporary executables, never overwriting the developer's running CLI or publishing a release.
6 changes: 3 additions & 3 deletions docs/mcp-agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

Use Codex or Claude Code on the **same computer** as the Jevwise MCP server. Jevwise serves Streamable HTTP at a literal-loopback `/mcp` endpoint, not stdio or legacy `/sse`. Do not register `jevwise mcp` as a stdio command or expose it through a public tunnel.

MCP is available in v1.1.0. Recorded PID/schema-2 state requires v1.2.0 or newer; see [release availability](../README.md#local-mcp).
MCP is available in v1.1.0. Recorded PID/schema-2 state requires v1.2.0 or newer; see [release availability](mcp.md).

The commands below use the renamed checkout CLI, `jevwise`; published releases through v1.2.0 use `jev`.

## 1. Start Jevwise in the server terminal

Prerequisites:

- Install/build `jevwise` and configure the Jev provider as in the [README](../README.md#build-and-run).
- Prepare a random, separate agent bearer token in a protected current-user-only file. Follow the [token and runtime permissions](../README.md#credentials-state-and-safety), including Windows ACL requirements.
- Install/build `jevwise` and configure the Jev provider using the [installation guide](../README.md#installation) and [configuration guide](configuration.md).
- Prepare a random, separate agent bearer token in a protected current-user-only file. Follow the [token and runtime permissions](mcp.md#credentials-state-and-safety), including Windows ACL requirements.
- Replace the example token-file path below with your own existing protected file. Do not reuse your upstream API key or the runtime management key.

```sh
Expand Down
74 changes: 74 additions & 0 deletions docs/mcp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Local MCP

[Documentation index](../README.md#documentation)

MCP is included in v1.1.0; diagnostic PID tracking and schema-2 state are included in v1.2.0. The executable rename is newer than those releases.

| Capability | v1.0.0 | v1.1.0 | v1.2.0 | Current checkout |
| --- | --- | --- | --- | --- |
| Foreground and background MCP | Not included | Included | Included | Included |
| Authenticated status and stop | Not included | Included | Included | Included |
| Recorded child PID and schema-2 state | Not included | Not included | Included | Included |

The examples below describe the current checkout, using synthetic paths and addresses. They are not services started by this documentation.

### Start and manage

Configure upstream credentials as for `jevwise decide`. Separately prepare a random agent bearer token in a protected, current-user-only file. Do not reuse your API key.

```sh
# Synthetic examples: supply your own protected token file before running.
jevwise mcp --token-file /private/example/agent-token --listen 127.0.0.1:8080
jevwise mcp --background --token-file /private/example/agent-token
jevwise mcp status
jevwise mcp stop
```

| Command or flag | Behavior |
| --- | --- |
| `jevwise mcp` | Foreground server; prints the public `/mcp` endpoint after startup. Ctrl+C stops it. |
| `--background` | Starts a terminal-independent child after authenticated readiness and ownership checks. |
| `jevwise mcp status` | Authenticates the exact instance and reports its startup/running state. v1.2.0 and newer also show the recorded PID when available. |
| `jevwise mcp stop` | Authenticates the exact instance and requests shutdown; an acknowledgement is not a promise that cleanup has already finished. |
| `--listen` | Literal loopback IP and nonzero port; default `127.0.0.1:8080`. |
| `--runtime-dir` | Private background state directory; default `os.UserCacheDir()/jevwise-mcp`. Use the same override for start, status and stop. |
| `--token-file` | Explicit protected agent-token file, overriding `JEV_MCP_TOKEN`; unreadable or unsafe files fail without fallback. |

- Without `--token-file`, startup reads `JEV_MCP_TOKEN`. Tokens must be nonempty printable ASCII without whitespace; a file may end in one LF or CRLF. No secret-valued token flag exists.
- Keep credentials out of command arguments and shell history. Use protected files or appropriately secured environment configuration instead of `--api-key`.
- `status` and `stop` do not load decision configuration, API keys, the decision service or the agent token file. They use the private runtime management key, with no upstream API request.
- Background mode does not install a login item, reboot service or automatic restart. On Windows, a restrictive parent Job Object can prevent detachment or terminate the child when its parent exits.
- A repeated background start returns an endpoint only for an authenticated running instance. If it is still preparing or stopping, wait and check status instead.
- Stop and restart after changing credentials, decision configuration or the executable. Updating a binary does not change an already-running process.
- PID support (v1.2.0 and newer): background children save their own PID in private `<runtime-dir>/state.json`; authenticated status prints `PID: NUMBER (inspection only)`. Normal owned shutdown removes that state along with the key and lease. PID reuse means this number is not proof of ownership or liveness: use `jevwise mcp stop`, not a blind PID-based kill.
- State format (v1.2.0 and newer): updated children write schema-2 state. The updated CLI still manages schema-1 instances without displaying a PID; older CLIs, including v1.1.0, cannot read schema-2 state. Stop an old instance before upgrading/restarting, and use the matching or newer CLI to manage a new child.

### Agent connection and limits

For token-safe Codex and Claude Code setup, discovery checks and cleanup, follow the [local agent onboarding guide](mcp-agents.md).

Connect an HTTP-capable local agent to `http://127.0.0.1:8080/mcp`, supplying `Authorization: Bearer <agent-token>`. The only tool is `decide`: send `prompt` and at least two distinct `options`. Results contain Jev probabilities and optional usage; ordinary decisions can incur upstream API charges.

- Stateless Streamable HTTP uses POST with SSE responses, not legacy `/sse` or standalone GET streams. GET/DELETE do not create sessions. Supported revisions: 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05; JSON-RPC batches are rejected.
- Only literal loopback addresses are supported. Requests need the exact configured Host and, when supplied, the exact `http://IP:port` Origin. Proxy headers do not establish trust; this is not a public-network deployment mode.
- Limits: 1 MiB encoded request, 8 MiB encoded decision JSON and 32 MiB SSE event. Escaped text and structured output appear together, so an event can be larger than the decision JSON. Go SDK clients needing maximum-size results should set `StreamableClientTransport.MaxEventSize: 32 << 20`; its default is 16 MiB.
- Four immediate request slots prevent queued decisions. Requests beyond capacity are rejected, not delayed for a later paid call.
- `--timeout` bounds each decision, including retries and waits, not daemon lifetime. Modern request disconnect cancels work; older protocols rely on decision deadlines and lifetime shutdown. Shutdown cancels active decisions and drains connections for up to five seconds.
- Services, startup callbacks and output writers must cooperate with cancellation. Blocking caller code and filesystem calls cannot be forcibly interrupted safely.

### Credentials, state and safety

| Credential | Purpose | Where it belongs |
| --- | --- | --- |
| Upstream API key | Server-to-Jev decisions | Existing decision configuration; never give it to the agent. |
| Agent bearer token | Agent-to-local `/mcp` access | `JEV_MCP_TOKEN` or an explicit protected token file. |
| Management key | Status/stop and instance verification | Separate private runtime file; never give it to the agent or transmit it as a bearer token. |

- Background bootstrap passes secrets through owned anonymous pipes, not child arguments, public state or logs. State contains bounded instance metadata, never credentials; the post-v1.1.0 checkout also records an inspection-only PID. Agent and API credentials must differ; the management key is separately generated.
- Management requests and responses use nonce-bound HMAC proofs for the exact instance, method and path. The private control key itself is never transmitted. Status/stop do not trust PIDs, signal unrelated processes or follow HTTP redirects/proxies.
- Fresh nonces prevent accepting replayed responses. Signed requests are not replay-cached; stop is idempotent and bound to the exact instance.
- Child stdout/stderr are discarded, with no retained output logs. Diagnostics are bounded, fixed safe messages through authenticated status/startup handling; prompts and arbitrary backend errors are not logged.
- Runtime files and token files must be owner-private. Unsafe permissions, links or ACLs are refused, not repaired. macOS refuses extended ACLs; Windows requires a protected current-user-only runtime-directory DACL and protected token-file DACLs. Background publication requires filesystem hardlink support and refuses unsupported storage.
- Existing locks, partial state, mismatched instances and unverified listeners require manual inspection. No automatic stale-lock deletion or PID-based kill occurs. Do not delete a runtime directory until you have established that its instance is stopped.
- Trusted runtime parent directories must already exist; startup creates only the private runtime leaf.
- Filesystem protection assumes caller-trusted runtime/token parent directories and ordinary cooperative local storage. It does not defend against hostile mount changes or ancestor-directory renaming. Local HTTP is not TLS, and same-user compromise is outside this boundary.
Loading
Loading