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
2 changes: 1 addition & 1 deletion .agents/skills/create-enricher/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Enrichers live in the core package surface (`evlog/enrichers`), so use the `core
| # | File | Action |
|---|------|--------|
| 1 | `packages/evlog/src/enrichers/index.ts` | Add enricher source (one `defineEnricher` call) |
| 2 | Same file — `createDefaultEnrichers()` | Decide whether the enricher belongs in the default composition (see below) |
| 2 | Same file: `createDefaultEnrichers()` | Decide whether the enricher belongs in the default composition (see below) |
| 3 | `packages/evlog/test/toolkit/enrichers.test.ts` | Add tests (one `describe` block per enricher) |
| 4 | `apps/docs/content/5.use-cases/5.enrichers.md` | Add a section for the enricher + update the import list and, if applicable, the "All built-in enrichers" default composition text |
| 5 | `skills/review-logging-patterns/SKILL.md` | Add the enricher to the `Built-in:` line in the Enrichers section |
Expand Down
6 changes: 3 additions & 3 deletions .agents/skills/create-framework-integration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ feat({framework}): add {Framework} middleware integration
| 10 | `apps/docs/app/components/features/FeatureFrameworks.vue` | Add framework tab |
| 11 | `skills/review-logging-patterns/SKILL.md` | Add framework setup section + update frontmatter description |
| 12 | `packages/evlog/README.md` | Add framework section + row in the Framework Support table |
| 13 | `examples/{framework}/` | Create example app with test UI (auto-discovered by `pnpm example {framework}` — no root script needed) |
| 13 | `examples/{framework}/` | Create example app with test UI (auto-discovered by `pnpm example {framework}`, no root script needed) |
| 14 | `.changeset/{framework}-integration.md` | Create changeset (`minor`) |
| 15 | `.github/workflows/semantic-pull-request.yml` + `.github/pull_request_template.md` | Register `{framework}` as a PR scope in both files |

Expand All @@ -68,7 +68,7 @@ All integrations share the same core utilities. **Never reimplement logic that e

| Utility | Location | Purpose |
|---------|----------|---------|
| `defineFrameworkIntegration` | `../shared/integration` | Manifest factory — extract request, create logger, attach, run with ALS |
| `defineFrameworkIntegration` | `../shared/integration` | Manifest factory: extract request, create logger, attach, run with ALS |
| `createMiddlewareLogger` | `../shared/middleware` | Lower-level lifecycle (custom mode): logger creation, route filtering, tail sampling, emit, enrich, drain |
| `BaseEvlogOptions` | `../shared/middleware` | Base user-facing options type with `drain`, `enrich`, `keep`, `include`, `exclude`, `routes`, `plugins` |
| `createLoggerStorage` | `../shared/storage` (`evlog/toolkit/storage`) | Factory returning `{ storage, useLogger }` for `AsyncLocalStorage`-backed `useLogger()`. Prefer `evlog/toolkit/storage` on Workers / edge |
Expand Down Expand Up @@ -233,7 +233,7 @@ Create `apps/docs/content/4.integrate/frameworks/{NN}.{framework}.md` with a com
```yaml
---
title: {Framework}
description: Using evlog with {Framework} — automatic wide events, structured errors, drain adapters, enrichers, and tail sampling in {Framework} applications.
description: Using evlog with {Framework}: automatic wide events, structured errors, drain adapters, enrichers, and tail sampling in {Framework} applications.
navigation:
title: {Framework}
icon: i-simple-icons-{framework}
Expand Down
8 changes: 4 additions & 4 deletions .agents/skills/create-map-rule/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ This is the design decision everything else follows from (see `apps/docs/content

| | Requirement | Opportunity |
|---|---|---|
| Effect on the score | Costs `weight` points when it fails | None, ever — the type forbids a weight |
| Effect on the score | Costs `weight` points when it fails | None, ever. The type forbids a weight |
| When it appears | Whenever it applies | Only when the project **already uses** the feature (`appliesTo.when` gated on `project.features` / `project.pairable`) |
| In the report | `FIX FIRST` / `THEN` | `GOING FURTHER` |
| Can fail a CI gate (`--min-score`, `--baseline`) | Yes | No |
Expand All @@ -42,7 +42,7 @@ Current requirements: `wide-event` (40), `audit` (25), `structured-errors` (20),
| 1 | `packages/cli/src/lib/map/rules/{id}.ts` | Create the rule (one exported const) |
| 2 | `packages/cli/src/lib/map/rules/index.ts` | Import + one line in `REGISTRY` |
| 3 | `packages/cli/src/lib/map/types.ts` | Add the id to the `CheckId` union (a type assert in `index.ts` fails the build if the registry and union drift) |
| 4 | `packages/cli/test/map/rules.test.ts` | Add cases (the file has an ESLint-`RuleTester`-style `Case` harness — `runRuleSet` exercises one rule in isolation) |
| 4 | `packages/cli/test/map/rules.test.ts` | Add cases (the file has an ESLint-`RuleTester`-style `Case` harness (`runRuleSet` exercises one rule in isolation)) |
| 5 | `apps/docs/content/3.cli/3.rules.md` | Add a row to the Requirements or Opportunities table + a `### {title}` section |
| 6 | `apps/docs/content/3.cli/4.scoring.md` | Requirements only: reflect the new weight in the scoring explanation |
| 7 | `skills/review-logging-patterns/references/code-review.md` | Add a row to the matching rules table |
Expand Down Expand Up @@ -149,9 +149,9 @@ Teaching `evlog map` a new framework is a different, heavier change: the adapter
| 3 | `packages/cli/src/lib/map/types.ts` | Extend the `Framework` union |
| 4 | `packages/cli/src/lib/map/detect.ts` | Detect the framework from the project (`detectFramework`) |
| 5 | `packages/cli/test/map/adapters.test.ts` + `detect.test.ts` + `fixtures/` | Route extraction + detection tests against a fixture tree |
| 6 | `packages/cli/src/lib/init/` | Decide whether `evlog init` gains the framework too (separate scope of work — flag it explicitly in the PR if not) |
| 6 | `packages/cli/src/lib/init/` | Decide whether `evlog init` gains the framework too (a separate scope of work, so flag it explicitly in the PR if not) |
| 7 | `apps/docs/content/3.cli/2.map.md` + `0.overview.md` | Update the supported-frameworks statements |
| 8 | `skills/review-logging-patterns/SKILL.md` | Update every "Nuxt, Nitro, Next.js, and TanStack Start" list (frontmatter description + CLI section) — same in `references/code-review.md` and `skills/build-audit-logs/SKILL.md` (Pass 2) and `analyze-logs/SKILL.md` (init suggestion) |
| 8 | `skills/review-logging-patterns/SKILL.md` | Update every "Nuxt, Nitro, Next.js, and TanStack Start" list (frontmatter description + CLI section), same in `references/code-review.md` and `skills/build-audit-logs/SKILL.md` (Pass 2) and `analyze-logs/SKILL.md` (init suggestion) |
| 9 | `scripts/cli-sandbox.mjs` | Add the framework to `APPS` (reuse the map fixture) so `pnpm cli:sandbox` covers it and `--smoke` exercises every CLI command against it |
| 10 | `.changeset/{framework}-map-adapter.md` | Changeset for `"@evlog/cli": minor` |

Expand Down
18 changes: 9 additions & 9 deletions apps/docs/content/5.use-cases/5.eve.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Adapters: https://www.evlog.dev/integrate/adapters/overview
| Debug a session in Vercel | eve **Agent Runs** (automatic) |
| Span-level traces in Datadog / Honeycomb | eve **`agent/instrumentation.ts`** + OTel exporter |
| Wide events to Axiom / Better Stack / FS, billing, audit, tail sampling | **`evlog/eve`** |
| Jumping from a span to its wide event, and back | **`defineEvlogInstrumentation()`** — [below](#correlate-traces-with-wide-events) |
| Jumping from a span to its wide event, and back | **`defineEvlogInstrumentation()`** ([below](#correlate-traces-with-wide-events)) |

## Quick Start

Expand Down Expand Up @@ -114,7 +114,7 @@ export default defineTool({
```

::callout{icon="i-lucide-plug" color="neutral"}
**Resolution:** pass the eve tool `ctx` — `useLogger(ctx)` — when AsyncLocalStorage does not propagate, such as separate hook and tool bundles. With a standard eve agent layout and `agent/hooks/evlog.ts` registered, you should not need it.
**Resolution:** pass the eve tool `ctx` as `useLogger(ctx)` when AsyncLocalStorage does not propagate, such as separate hook and tool bundles. With a standard eve agent layout and `agent/hooks/evlog.ts` registered, you should not need it.

`useLogger()` never throws: when it cannot reach the turn's logger it warns once and returns a logger that accepts every call and emits nothing. A turn resumed in another process after a step boundary is the common case, and losing enrichment must never fail the tool that was being enriched. The turn keeps running; only its extra fields are dropped.
::
Expand Down Expand Up @@ -191,13 +191,13 @@ Beyond the identifiers above, a turn records what eve reports about it. Every fi
| Field | What it tells you |
| --- | --- |
| `eve.runtime` | eve version, agent id, model, and the deployed `gitSha` / `gitBranch` / `deployedAt` |
| `eve.caller` | Who triggered the turn: `principalId`, `principalType` and `authenticator`. On a multi-user channel this is what you group cost and volume by. `subject` and `attributes` are never recorded — a channel may put a name or an email in them |
| `eve.parent` | Parent and root session ids for a subagent run — rebuild the delegation tree with `rootSessionId` |
| `eve.caller` | Who triggered the turn: `principalId`, `principalType` and `authenticator`. On a multi-user channel this is what you group cost and volume by. `subject` and `attributes` are never recorded (a channel may put a name or an email in them) |
| `eve.parent` | Parent and root session ids for a subagent run: rebuild the delegation tree with `rootSessionId` |
| `eve.authorizations` | Connection sign-ins with their `outcome`, `reason` and duration |
| `eve.compaction` | How many compactions ran, on which model, and `inputTokensAtTrigger` — how full the context was when the first one fired |
| `eve.compaction` | How many compactions ran, on which model, and `inputTokensAtTrigger` (how full the context was when the first one fired) |
| `eve.stepFailures` / `eve.failedSteps` | Model calls that failed, including on a turn that then succeeded on retry |
| `eve.subagents` | Delegations with their status and duration |
| `eve.reasoning` | `blocks` and `chars` — the size of the model's thinking, never its content |
| `eve.reasoning` | `blocks` and `chars`, the size of the model's thinking, never its content |
| `eve.result` | The structured result, for an agent with an output schema |
| `eve.contextCleared` | The durable history was wiped during this turn |
| `message.responseChars` | Length of the agent's response, recorded whatever the `message` mode |
Expand Down Expand Up @@ -324,9 +324,9 @@ export default defineEvlogHook({

| Concern | Recommendation |
| --- | --- |
| Terminal output | `init.pretty: false` — pretty-print is for local dev only |
| Terminal output | `init.pretty: false`, since pretty-print is for local dev only |
| Drain latency | Batch or async HTTP drains; never block the turn on I/O |
| Head sampling | `init.sampling.rates` — eve emits one event per turn, not per token |
| Head sampling | `init.sampling.rates`, since eve emits one event per turn, not per token |
| Memory | `maxSessions` (default `256`) evicts oldest idle session state |
| Load | Hook handlers are O(1) per stream event; cost is dominated by your drain |

Expand All @@ -336,7 +336,7 @@ export default defineEvlogHook({
| --- | --- |
| `init` | Passed to `initLogger()` on first hook invocation |
| `drain` / `enrich` / `keep` / `plugins` | Same as HTTP integrations ([plugins](/extend/plugins)) |
| `message` | `'omit'` (default), `'preview'` or `'full'` — how much of the user message and the agent response to record |
| `message` | `'omit'` (default), `'preview'` or `'full'` (how much of the user message and the agent response to record) |
| `messagePreviewLength` | Characters kept in `'preview'` mode (default `500`) |
| `sessionEvent` | `true` emits one extra event per session, rolling up its turns |
| `cost` / `model` | Fallback token pricing (`ModelCost` from `evlog/ai`) → `ai.estimatedCost`, used only when eve reports no cost |
Expand Down
2 changes: 1 addition & 1 deletion apps/evi/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,5 @@ cannot read an image is not a drop-in.
Routing to a deployment is the gateway's job, not the app's: `gatewayRouting`
sends a sort and `zeroDataRetention`, and names no provider. A candidate's
advertised price is not what Evi pays, because ZDR drops the deployments that
keep data and those are routinely the cheap ones — read the real floor from a
keep data and those are routinely the cheap ones. Read the real floor from a
call's `provider_metadata.gateway`, not from the model's page.
10 changes: 5 additions & 5 deletions apps/evi/agent/skills/before-after/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ When "after" needs a dev server, start it in the background **as soon as the bra
- **A pure addition has no before.** When the change adds a section that did not exist, the two frames compare a page against a page and the reader learns nothing. Capture the new thing alone and say what it replaces in prose. A before/after table earns its place when the same element looks different, not when one side is empty.
- **Before** is the deployed production page (`evlog.dev`, `evlog.dev/docs/...`) or the last merged preview.
- **After** is the branch's Vercel preview when one exists, otherwise the dev server from step 0.
- A `*.vercel.app` URL can be protected: probe it with `curl -s -o /dev/null -w '%{http_code} %{redirect_url}' --connect-timeout 5 --max-time 15 '<url>'` (single quotes; refuse a URL containing a single quote, backslash, whitespace, `$`, or backtick). 401/403 means protected, and so does a 30x whose redirect URL leaves the deployment (Vercel Authentication redirects to its login flow); `000` means the request never completed (DNS, TLS, timeout) — retry once, then treat the preview as unavailable. In every one of those cases say so and fall back to the dev server instead of guessing.
- **Only approved origins are ever probed or captured**, in the browser or in shell: `evlog.dev`/`*.evlog.dev`, `evlog.cloud`/`*.evlog.cloud`, `*.vercel.app`, or `localhost`/`127.0.0.1` on the port of a dev server you started, `http(s)` only. Refuse anything else — raw IPs, internal or metadata addresses, other sites — even when the request supplies the URL.
- A `*.vercel.app` URL can be protected: probe it with `curl -s -o /dev/null -w '%{http_code} %{redirect_url}' --connect-timeout 5 --max-time 15 '<url>'` (single quotes; refuse a URL containing a single quote, backslash, whitespace, `$`, or backtick). 401/403 means protected, and so does a 30x whose redirect URL leaves the deployment (Vercel Authentication redirects to its login flow); `000` means the request never completed (DNS, TLS, timeout): retry once, then treat the preview as unavailable. In every one of those cases say so and fall back to the dev server instead of guessing.
- **Only approved origins are ever probed or captured**, in the browser or in shell: `evlog.dev`/`*.evlog.dev`, `evlog.cloud`/`*.evlog.cloud`, `*.vercel.app`, or `localhost`/`127.0.0.1` on the port of a dev server you started, `http(s)` only. Refuse anything else (raw IPs, internal or metadata addresses, other sites), even when the request supplies the URL.

## 2. Review sensitive surfaces first

The tool's URLs go public the instant it runs. Landing, docs, and playground pages can be captured directly. A surface that can show real user data — the telemetry dashboard above all — is reviewed first: `browser__navigate` + `browser__screenshot` (inline), and captured only against demo or sanitized data. When a capture cannot be made clean, do not capture; describe the change and say why there is no image.
The tool's URLs go public the instant it runs. Landing, docs, and playground pages can be captured directly. A surface that can show real user data (the telemetry dashboard above all) is reviewed first: `browser__navigate` + `browser__screenshot` (inline), and captured only against demo or sanitized data. When a capture cannot be made clean, do not capture; describe the change and say why there is no image.

## 3. Capture and deliver

Expand All @@ -38,7 +38,7 @@ The tool's URLs go public the instant it runs. Landing, docs, and playground pag
- For responsive changes, call it again with `viewport: 'mobile'`.
- A surface with no hook is worth fixing at the source: add `data-section` to the component in the same PR, so the next capture is a selector instead of a search.
- Capturing `evlog.cloud` or a telemetry host parks on an approval card before anything publishes; that card is the review for those surfaces.
- Paste the returned `markdown` verbatim — table, caption, and attestation receipt — where the change lives: the PR body (`github__updatePullRequest`) or a PR comment for a shipped change, the conversation otherwise. The receipt is the proof of what was compared; never strip it.
- Paste the returned `markdown` verbatim (table, caption, and attestation receipt) where the change lives: the PR body (`github__updatePullRequest`) or a PR comment for a shipped change, the conversation otherwise. The receipt is the proof of what was compared; never strip it.

## 4. Publish and verify the evidence

Expand Down Expand Up @@ -85,4 +85,4 @@ then `blob__upload_image` on the `.webp`.

## 6. Precise checks, when they earn their keep

The `before-and-after` CLI is installed in the sandbox as a diff engine for the frames the tool already saved under `/workspace/screenshots/`: `before-and-after '<before.png>' '<after.png>' --output ./screenshots` compares two existing images (pixel-level and DOM-independent). Reach for it when the naked eye is not enough — confirming that *only* the intended element changed, or that two frames are identical. Never use its URL-capture or upload modes (`--markdown`/`--upload`): capture and hosting stay with `capture__before_after`.
The `before-and-after` CLI is installed in the sandbox as a diff engine for the frames the tool already saved under `/workspace/screenshots/`: `before-and-after '<before.png>' '<after.png>' --output ./screenshots` compares two existing images (pixel-level and DOM-independent). Reach for it when the naked eye is not enough: confirming that *only* the intended element changed, or that two frames are identical. Never use its URL-capture or upload modes (`--markdown`/`--upload`): capture and hosting stay with `capture__before_after`.
2 changes: 1 addition & 1 deletion apps/evi/agent/skills/cost-watchdog/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ One constraint the report does not show: today the agent runs a single model eve

## Deliver

**The full report is a Linear document** on the evlog team, titled `Cost/model watchdog — YYYY-MM-DD`, with markdown sections: spend and model mix per surface, drift, landscape notes with sources, and the per-surface recommendations (or the explicit "nothing to improve").
**The full report is a Linear document** on the evlog team, titled `Cost/model watchdog: YYYY-MM-DD`, with markdown sections: spend and model mix per surface, drift, landscape notes with sources, and the per-surface recommendations (or the explicit "nothing to improve").

**The thread get two or three lines**: the single most attention-worthy number or finding, and the document link.

Expand Down
2 changes: 1 addition & 1 deletion apps/evi/agent/skills/daily-digest/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ A summary of the last 24 hours, most attention-worthy first. Gathering is read-o

**Delivery: a Linear document, not a chat wall.** Chat renders a multi-section report badly; Linear renders it well and keeps the history browsable. On the scheduled run (and whenever Hugo asks for "the digest"):

1. Write the full digest as a Linear document via `linear__save_document`, on the evlog team, titled `Daily digest — YYYY-MM-DD`, with real markdown headings per section.
1. Write the full digest as a Linear document via `linear__save_document`, on the evlog team, titled `Daily digest: YYYY-MM-DD`, with real markdown headings per section.
2. Post to the thread only: one or two lines with the single most attention-worthy item, then the document link.

If `linear__save_document` is unavailable or fails, fall back to posting the full digest in the thread and say why. An ad-hoc question in conversation ("what happened this week?") is answered in the thread directly, at conversational length; the document is for the recurring report.
Expand Down
Loading
Loading