diff --git a/.fallowrc.json b/.fallowrc.json index 4ceaf9526..529aa3531 100644 --- a/.fallowrc.json +++ b/.fallowrc.json @@ -16,6 +16,9 @@ // `new Worker(new URL('./layout.worker.ts', import.meta.url))` in // apps/agent-forge/src/canvas/layout.ts (C17). "apps/agent-forge/src/canvas/layout.worker.ts", + // The packaged Studio's `lousho-studio` bin and its pack-time SDK-range pin. + "apps/agent-forge/server/cli.ts", + "apps/agent-forge/scripts/pin-sdk-range.mjs", // The widget bundle (X04b): vite.widget.config.ts's // lib.entry is not visible to the import graph. "apps/agent-forge/src/widget/entry.ts", @@ -100,7 +103,7 @@ "@ai-sdk/anthropic", "@ai-sdk/google", "@ai-sdk/openai", - "lousho", + "@linuxdevil/lousho", "ollama-ai-provider", "ollama-ai-provider-v2", "prompts" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f50328cd3..8b0099ab1 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -31,7 +31,7 @@ What did you run, and what did it show? Name anything you couldn't verify, such ## Release -Merging doesn't publish. The Release workflow publishes `lousho` and `create-lousho` to npm as `latest` every Monday at 06:00 UTC, or when run by hand, so everyone on a default install gets this change in the next release. The `Unreleased` entry above becomes that release's notes. +Merging publishes `@linuxdevil/lousho` and `create-lousho` to npm as `latest`, so everyone on a default install gets this change. - [ ] This PR doesn't need a release on its own, so I added the `norelease` label. The weekly run skips when every PR since the last release has it diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a15e953d..19c3dec76 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,9 +51,7 @@ jobs: - run: npm run registry:check # schemas/ is generated from the workspace contracts; fail if stale. - run: npm run workspaces:schema:check - # Agent Forge imports the SDK as `lousho` via `file:../..`, i.e. the root dist/ built above. - - run: npm run typecheck --workspace apps/agent-forge - - run: npm run typecheck:server --workspace apps/agent-forge + # Agent Forge typechecks moved to the dedicated `studio` job (W15a). zod4: runs-on: ubuntu-latest steps: @@ -66,8 +64,11 @@ jobs: - run: npm install --no-save zod@4 - run: npx tsc --noEmit - run: npm run typecheck:tests - forge: + # @linuxdevil/studio (apps/agent-forge): its own package since W13. Builds the SDK first (Studio depends on it through + # file:../.. in the monorepo), then Studio's checks, then packs both and smoke-tests the install. + studio: runs-on: ubuntu-latest + timeout-minutes: 25 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -75,11 +76,67 @@ jobs: node-version: '22.19.0' cache: 'npm' - run: npm ci - - run: npm run build -- --no-dts - - run: npm run test --workspace apps/agent-forge -- --run - - run: npm run test:server --workspace apps/agent-forge + - run: npm run build + env: + NODE_OPTIONS: --max-old-space-size=12288 + - run: npm run typecheck --workspace apps/agent-forge + - run: npm run typecheck:server --workspace apps/agent-forge # Studio imports only public SDK exports (PLAN §19.7). - run: npm run lint:imports --workspace apps/agent-forge + - run: npm run test --workspace apps/agent-forge -- --run + - run: npm run test:server --workspace apps/agent-forge + # W15b: the same bridge-compat matrix against the SDK in this commit. + - run: npm run test:bridge-compat --workspace apps/agent-forge + - run: npm run build:studio + # Packs the SDK and @linuxdevil/studio, dry-run publishes Studio, installs both in a fresh project, and checks + # that `lousho-studio` and `lousho studio` serve the UI. Never publishes. + - run: npm run pack-smoke:studio -- --skip-build + # W15b: the same bridge-compat matrix against the OLDEST SDK release Studio + # still supports (MIN_BRIDGE_SDK_VERSION in server/host/sdkCompat.ts). When + # that version is not on npm yet the job skips with a notice; once it is + # published this runs the suite against the real installed package. + bridge-compat-min: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22.19.0' + cache: 'npm' + - run: npm ci + - run: npm run build + env: + NODE_OPTIONS: --max-old-space-size=12288 + - name: Resolve MIN_BRIDGE_SDK_VERSION + id: min + run: | + v=$(npx tsx -e "import { MIN_BRIDGE_SDK_VERSION } from './apps/agent-forge/server/host/sdkCompat.ts'; console.log(MIN_BRIDGE_SDK_VERSION)") + echo "version=$v" >> "$GITHUB_OUTPUT" + - name: Check the minimum SDK is published + id: pub + env: + MIN: ${{ steps.min.outputs.version }} + run: | + if npm view "@linuxdevil/lousho@$MIN" version > /dev/null 2>&1; then + echo "published=true" >> "$GITHUB_OUTPUT" + else + echo "published=false" >> "$GITHUB_OUTPUT" + echo "::notice::@linuxdevil/lousho@$MIN is not published yet - bridge-compat-min skips until it is." + fi + - name: Install the minimum SDK + if: steps.pub.outputs.published == 'true' + env: + MIN: ${{ steps.min.outputs.version }} + run: | + mkdir -p "$RUNNER_TEMP/min-sdk" && cd "$RUNNER_TEMP/min-sdk" + npm init -y > /dev/null + npm install "@linuxdevil/lousho@$MIN" ai@7 zod@4 --no-audit --no-fund + - name: Run the compat suite against it + if: steps.pub.outputs.published == 'true' + env: + MIN: ${{ steps.min.outputs.version }} + LOUSHO_BRIDGE_SDK_DIR: ${{ runner.temp }}/min-sdk/node_modules/@linuxdevil/lousho + run: npm run test:bridge-compat --workspace apps/agent-forge test: runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index 12480d960..dd7bc06eb 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,6 @@ snip.txt # Author eval run outputs (npm run eval:author --workspace apps/agent-forge). # baseline.json is committed and lives one level up at evals/author/. apps/agent-forge/evals/author/results/*.json + +# Studio pack-time SDK-range pin backup +.package.json.prepack diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cf81d763..00417628e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,898 +1,904 @@ -# Changelog - -All notable changes to @lousho/build-ai-agent will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -- New `lousho/observe` subpath: `startObserveDaemon`, `probeHealth`, `isObserveDaemon`, `defaultDbPath`, `OBSERVE_DEFAULT_PORT` and the daemon option/retention defaults, so a host process (Studio) can run and probe the collector through public exports. -- `lousho bridge`: a new command that speaks Studio's bridge protocol (NDJSON on stdin/stdout, protocol 1) so Lousho Studio can build and run a project's agents with that project's own SDK and dependencies. It answers `hello`, `catalog`, `compile` and `shutdown`, and runs agents: `run`/`chat` start a run or chat turn, `steer`/`abort` control it, `resolveApproval`/`answerQuestion` resume a paused run, and every `AgentEvent`, trace span, steer join and run settle arrives as a pushed event (flows are refused with `LOUSHO_HOST_UNSUPPORTED`). -- Model strings resolve any official AI SDK provider: `mistral/...`, `groq/...`, `xai/...`, `bedrock/...`, `vertex/...`, `azure/...` and 13 more load `@ai-sdk/` on the first call, and `ai-sdk//` loads any other `@ai-sdk/*` package. The package reads its own credentials. See [Providers](docs/providers.md#any-ai-sdk-vendor-by-name). - -## [1.0.0-alpha.117] - 2026-11-09 - -## [1.0.0-alpha.116] - 2026-11-09 - -## [1.0.0-alpha.115] - 2026-11-09 - -## [1.0.0-alpha.114] - 2026-11-09 - -## [1.0.0-alpha.113] - 2026-11-09 - -## [1.0.0-alpha.112] - 2026-11-09 - -## [1.0.0-alpha.111] - 2026-11-09 - -## [1.0.0-alpha.110] - 2026-11-09 - -## [1.0.0-alpha.109] - 2026-11-09 - -## [1.0.0-alpha.108] - 2026-11-09 - -## [1.0.0-alpha.107] - 2026-11-09 - -## [1.0.0-alpha.106] - 2026-11-09 - -## [1.0.0-alpha.105] - 2026-11-09 - -## [1.0.0-alpha.104] - 2026-11-09 - -## [1.0.0-alpha.103] - 2026-11-08 - -## [1.0.0-alpha.102] - 2026-11-08 - -## [1.0.0-alpha.101] - 2026-11-08 - -## [1.0.0-alpha.100] - 2026-11-08 - -## [1.0.0-alpha.99] - 2026-11-08 - -## [1.0.0-alpha.98] - 2026-11-08 - -## [1.0.0-alpha.97] - 2026-11-08 - -## [1.0.0-alpha.96] - 2026-11-08 - -## [1.0.0-alpha.95] - 2026-11-08 - -## [1.0.0-alpha.94] - 2026-11-08 - -## [1.0.0-alpha.93] - 2026-11-08 - -## [1.0.0-alpha.92] - 2026-11-08 - -## [1.0.0-alpha.91] - 2026-11-08 - -## [1.0.0-alpha.90] - 2026-11-08 - -## [1.0.0-alpha.89] - 2026-11-08 - -## [1.0.0-alpha.88] - 2026-11-08 - -## [1.0.0-alpha.87] - 2026-11-08 - -## [1.0.0-alpha.86] - 2026-11-08 - -## [1.0.0-alpha.85] - 2026-11-08 - -## [1.0.0-alpha.84] - 2026-11-08 - -## [1.0.0-alpha.83] - 2026-11-08 - -## [1.0.0-alpha.82] - 2026-11-08 - -## [1.0.0-alpha.81] - 2026-11-08 - -## [1.0.0-alpha.80] - 2026-11-08 - -## [1.0.0-alpha.79] - 2026-11-08 - -## [1.0.0-alpha.78] - 2026-11-08 - -## [1.0.0-alpha.77] - 2026-11-08 - -## [1.0.0-alpha.76] - 2026-11-08 - -## [1.0.0-alpha.75] - 2026-11-08 - -## [1.0.0-alpha.74] - 2026-11-08 - -## [1.0.0-alpha.73] - 2026-11-08 - -## [1.0.0-alpha.72] - 2026-11-08 - -## [1.0.0-alpha.71] - 2026-11-08 - -## [1.0.0-alpha.70] - 2026-11-08 - -## [1.0.0-alpha.69] - 2026-11-08 - -## [1.0.0-alpha.68] - 2026-11-08 - -## [1.0.0-alpha.67] - 2026-11-08 - -## [1.0.0-alpha.66] - 2026-11-08 - -## [1.0.0-alpha.65] - 2026-11-08 - -## [1.0.0-alpha.64] - 2026-11-08 - -## [1.0.0-alpha.63] - 2026-11-08 - -## [1.0.0-alpha.62] - 2026-11-08 - -## [1.0.0-alpha.61] - 2026-11-08 - -## [1.0.0-alpha.60] - 2026-11-08 - -## [1.0.0-alpha.59] - 2026-11-08 - -## [1.0.0-alpha.58] - 2026-11-08 - -## [1.0.0-alpha.57] - 2026-11-08 - -## [1.0.0-alpha.56] - 2026-11-08 - -## [1.0.0-alpha.55] - 2026-11-08 - -## [1.0.0-alpha.54] - 2026-11-08 - -## [1.0.0-alpha.53] - 2026-11-07 - -## [1.0.0-alpha.52] - 2026-11-07 - -## [1.0.0-alpha.51] - 2026-11-07 - -## [1.0.0-alpha.50] - 2026-11-07 - -## [1.0.0-alpha.49] - 2026-11-07 - -## [1.0.0-alpha.48] - 2026-11-07 - -## [1.0.0-alpha.47] - 2026-11-07 - -## [1.0.0-alpha.46] - 2026-11-07 - -## [1.0.0-alpha.45] - 2026-11-07 - -## [1.0.0-alpha.44] - 2026-11-07 - -## [1.0.0-alpha.43] - 2026-11-07 - -## [1.0.0-alpha.42] - 2026-11-07 - -## [1.0.0-alpha.41] - 2026-11-07 - -## [1.0.0-alpha.40] - 2026-11-07 - -## [1.0.0-alpha.39] - 2026-11-07 - -## [1.0.0-alpha.38] - 2026-11-07 - -## [1.0.0-alpha.37] - 2026-11-07 - -## [1.0.0-alpha.36] - 2026-11-07 - -## [1.0.0-alpha.35] - 2026-11-07 - -## [1.0.0-alpha.34] - 2026-11-07 - -## [1.0.0-alpha.33] - 2026-11-07 - -## [1.0.0-alpha.32] - 2026-11-07 - -- Install docs use the public npm packages again (`npm install lousho`, `npm create lousho`). -- `lousho/improve` (experimental): `improveAgent()` proposes tested edits to an agent's instructions and skills, offline, gated against the current harness and held-out cases. It applies nothing. See [Improving an agent](docs/improve.md). -- `agent.recover({ retryWithDiagnosis: true })` gives a run that reached `maxAttempts` one fork told why it failed, before quarantining it. `createOutputCheck()` is a new hook that runs your check after a tool succeeds and tells the model what it found. A fork no longer inherits `lastError` and `attempts` from its source. -- **Behavior change:** `agent.recover()` no longer resumes a failing run forever. It backs off exponentially from the last failure (up to 15 minutes) and stops after `maxAttempts` failures in a row (default 10), calling the new `onQuarantined(run)` once. `agent.resume()` still runs a quarantined run. Pass a larger `maxAttempts` to keep retrying longer. -- `lousho observe failures` also lists runs whose process died and scores below `--min-score` (default 0.5). -- `agent.recover()` pauses during a provider outage instead of using up each run's attempts: after 3 provider failures in a row it stops resuming, probes with one run at growing intervals, and reports through the new `onOutage(paused)`. -- `createProgressGuard()` gains `maxSameResults` (default 5), which denies a tool that keeps returning the same result even when its arguments differ. Run spans record `lousho.run.finish_reason`, and `lousho observe failures` lists runs that ended without an answer and names rate limits. -- `lousho observe failures` lists recurring tool and model failures by agent, from the observe database or from trace files (`--traces`), each with a class and trace ids. See [Observability](docs/observability.md). -- `createProgressGuard()`: an opt-in hook that adds advice to repeated tool failures, denies calls that keep returning the same result and tools that keep failing the same way, and stops the run with `LOUSHO_RUN_NO_PROGRESS` if denials continue. Actions stream as the new `heal.action` event; tool hooks get `ctx.emit`, and tool spans record `lousho.tool.replaced_by_hook`. See [Hooks](docs/hooks.md#stop-a-run-that-makes-no-progress). - -## [1.0.0-alpha.31] - 2026-11-01 - -- **Behavior change:** the package is renamed from `@lousho/build-ai-agent` to `lousho`, and the scaffolder from `create-lousho-agent` to `create-lousho` (`npm create lousho`). Update imports from `@lousho/build-ai-agent` to `lousho` (subpaths such as `lousho/testing` keep their names). Install with `npm install lousho` or `npm create lousho` (no token needed). The packages are also mirrored to GitHub Packages as `@linuxdevil/lousho` and `@linuxdevil/create-lousho` for the repository's Packages tab. - -## [1.0.0-alpha.30] - 2026-10-31 - -What is on `main` and not yet on npm. Entries marked **Behavior change** can -break working code; [Upgrading](docs/upgrading.md) lists each one with its -opt-out. - -### Added - -- The `lousho observe` daemon: a local collector (loopback, `http://127.0.0.1:7777`, SQLite at `~/.lousho/observe.db`) that gathers every agent's traces, heartbeats and eval scores when `LOUSHO_OBSERVE` is set — no per-agent code changes. `lousho observe start|status|stop|rebuild-rollups|token create`; a second `start` on a busy port attaches when it is already the observe daemon. See [Observability](docs/observability.md#the-observe-daemon). -- Agent Forge is rebuilt around a left navigation grouped Build (Agents, Playground), Observe (Traces, Threads, Monitoring), Evaluate (Evaluations: Scores, Datasets, Evaluators, Annotation queues) and Assets (Prompts, Context Hub), plus Settings. It replaces the Build/Observe toggle and the old Observe tabs, and the Build toolbar shows only on Agents. See [Agent Forge](docs/agent-forge.md#navigation). -- Every Studio screen has a hash route with its filters in the query string (`#/traces?agent=…&status=error`, `#/traces/?obs=`), so a reload, a bookmark, a pasted link or back/forward lands on the same view. See [Routes and deep links](docs/agent-forge.md#routes-and-deep-links). -- Studio keyboard: `Ctrl+K` / `Cmd+K` opens a command palette (go to a screen, open an agent, open a pasted trace id, toggle the light/dark theme), `?` lists the shortcuts, and `j`/`k`/`Enter` move through and open table rows. See [Keyboard](docs/agent-forge.md#keyboard). -- The last Studio nav item shows whether the collector answers. While the daemon is down, each daemon screen shows the command to start it and fills in once it is back. Daemon screens read it through the token-gated `/observe/**` proxy on the studio server, which forwards reads plus only the writes the screens make. See [The collector](docs/agent-forge.md#the-collector-observe-daemon). -- Studio Traces and Trace screens: the list filters by search (name, input, output), agent, status, model, project, prompt and time range (15m to 30d or custom, default 24 h), sorts by duration, tokens or cost, and shows input/output previews, the error message and the model. A trace opens in one viewer (tree or waterfall, the failed step selected by default) whose inspector shows the error, input and output as chat bubbles with tool-call chips (Rendered, Raw or JSON), thumbs up/down feedback, usage and attributes; the header links the agent and thread and has **Copy link**, **Add to dataset** and **Add to queue**. Model-call steps carry **Open in Playground**. See [Traces](docs/agent-forge.md#traces). -- Studio Threads (`#/threads`, one row per session, a session's turns oldest first), Monitoring (`#/monitoring`: the fleet table with status, last heartbeat, runs today, error rate, p95 latency and dropped events, plus Runs, Latency p95, Error rate, Cost and Tokens charts over 1h to 30d whose buckets open the matching traces) and Scores (`#/evals/scores`: one row per eval with pass rate, trend, per-check pass rates and latest failures, filtered by eval, agent, source and range). See [Monitoring](docs/agent-forge.md#monitoring). -- Studio Playground (`#/playground`): system, user and assistant messages with `{name}` variables, a provider and model (`mock` with no key), tools borrowed from a saved agent, a streamed run with **Stop**, **Open trace** when it finishes, and **Save as new prompt version**. See [Playground](docs/agent-forge.md#playground). -- Playground deep links: `#/playground?trace=&fromObs=` loads a model call from a trace, and `#/playground?prompt=[@]` loads a registry prompt. -- `POST /playground/run` on the studio server runs a playground request and streams SSE `delta`, `tool_call`, `done` and `error` events; runs report to the daemon as agent `playground`. See [Playground run endpoint](docs/agent-forge.md#playground-run-endpoint). -- Studio Prompts (`#/prompts`: versions, labels, runs that used each version, a side-by-side diff, **Move `