Skip to content

W15b: bridge compatibility matrix (current + minimum SDK) - #1158

Closed
LinuxDevil wants to merge 429 commits into
fb-w15a-studio-cifrom
fb-w15b-bridge-compat
Closed

LinuxDevil wants to merge 429 commits into
fb-w15a-studio-cifrom
fb-w15b-bridge-compat

Conversation

@LinuxDevil

Copy link
Copy Markdown
Owner

Summary

  • apps/agent-forge/server/__tests__/bridge.compat.test.ts + server/vitest.compat.config.ts (npm run test:bridge-compat): drives the real WorkspaceHostSelector → BridgeAgentHost → lousho bridge path against the SDK under test — handshake (mode/protocol/specVersions/sdkVersion), catalog, scripted runs, approval continuation, abort, and unknown-model LOUSHO_* rejection. LOUSHO_BRIDGE_SDK_DIR selects the SDK; default is this checkout.
  • CI: the studio job runs the matrix against the SDK in the commit; new bridge-compat-min job installs @linuxdevil/lousho@MIN_BRIDGE_SDK_VERSION from npm and runs the same suite (skips with a notice until that version is published).
  • HostSelection now carries protocol/specVersions from the handshake so the suite can assert them; settle() compares them so the handshake result isn't shadowed by the provisional selection.
  • Merge of fb-w11c-host-status into the W15a/W13 line (72 conflicts resolved by hand), plus follow-ups: banner/test strings use @linuxdevil/lousho, specTypes stops double-exporting SecretRef (single export stays in shared/wireTypes), pack-smoke scripts share helpers via smokeKit, unused dagre devDependency dropped.

Test plan

  • npm run test:bridge-compat --workspace apps/agent-forge — 8/8 pass against the in-tree SDK
  • Studio + server typecheck, lint:imports, touched server/client test files
  • Studio server suite green (one known idleVersions debounce flake, passes in isolation)
  • fallow: dead-code and dupes clean; health findings are pre-existing ceilings + locally degraded coverage (Windows EPERM symlink tests excluded from the local coverage run; CI runs full coverage on Ubuntu)
  • bridge-compat-min job exercises the published minimum once @linuxdevil/lousho@<min> exists on npm

Generated with Devin

LinuxDevil and others added 30 commits November 9, 2026 10:45
…e its tick (#1008)

watchAgent gains onTick, called with every finished tick whether or not a
cycle ran, so a driver can run an exact number of ticks. A tick now waits
for a lease renewal in flight before it ends, and renewals no longer
overlap, so stop() releasing the lease can't be undone by a late renewal
that leaves the next watcher out.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…1009)

improve.json gains an editors block (proposers, mode, select, parallel)
that healOnce and watchAgent pass to improveAgent, with lousho improve
flags to override it and status showing it. With k > 1 a cycle estimates
its spend before the editors run. lousho improve approve now runs the
corpus guard right after the write and rolls back to the approved
revision's parent on failure, unless --no-guard.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…os (S28) (#1010)

examples/heal-benchmark/loopScenarios.ts drives the whole loop on
coding-agent through watchAgent: runs are recorded through a local observe
daemon, so they carry lousho.harness.revision, and are scored by the
hidden tests. Ten scenarios check that the loop fixes, refuses or undoes
what it must: a true fix kept by the monitor, a planted regression riding
on a real fix rejected at the gate, the planted harness forced through the
real applyProposal and rolled back to the base bytes by the corpus guard,
a no-op, a prompt injection in a source file, a healthy agent, the kill
switch mid-cycle, manual approval with a stale approve, the daily token
budget, and a stable revision the monitor must keep. Each attempt is one
row with its outcome, audit check, hashes, requests, tokens and pass^3.
Offline the agent and editors are scripted mock models; live, the agent
runs on qwen/qwen3.7-flash and the editors on stealth/crouton-alpha, which
may only read this loop's synthetic fixtures.

codingAgent() takes the files a project starts from, so a scenario can
plant text in src/months.js without it counting as a change.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Clicking a node's run status pill (or the Inspector's "View spans"
button) opens a right-side sheet listing only that node's spans — and
its subagent children's — of the live/last run in the shared
TraceViewer (tree/waterfall, step inspector, Open trace footer link).

- components/test/nodeSpans.ts: spansForNode filters the span feed by
  lousho.node.id (exact id or <id>/… children) and cuts parentIds that
  fall outside the matched set so filtered children become roots.
- components/test/NodeSpansSheet.tsx + nodeSpans.css: Sheet titled
  "Spans · <label>" with span count copy, empty state, local
  tree/waterfall mode and selection.
- state/useNodeSpans.ts + AppState: spansNodeId/openNodeSpans/
  closeNodeSpans; cleared on agent switch/create/rename/delete.
- canvas/NodeCard.tsx + AgentGroupNode.tsx + CanvasArea.tsx: a
  non-idle node's StatusPill becomes a nodrag button that calls
  openNodeSpans(id) with stopPropagation.
- components/Inspector.tsx: ghost "View spans" button (Activity
  icon), disabled with the run-first reason when the node has no spans.
- agents.css: .node-status-button pill-button styling (tokens only).
…un (#1011)

examples/heal-benchmark/loop.ts runs the S28 scenarios offline or with
--live, appends one row per attempt to --out, and resumes from it: rows
from the same mode are skipped, errored ones are rerun.
--offline-scenarios keeps chosen scenarios on the scripted models inside a
live run; their rows count as that run's and the report marks them
"live, scripted agent". Before each attempt and before every editor call
it checks that its observe database holds only this loop's agents and
coding-agent fixture inputs, so the editor model reads synthetic fixtures
only; it takes no --db.
armSummary.ts gains loopBars(), the S29 bars, and loopDecision(), the S30
rule: INCOMPLETE on too few counted attempts or mixed live and offline
runs, NO-GO on a planted-harm miss, GO with misses otherwise, GO when
every bar holds. Attempts that couldn't test anything are marked
precondition and never count either way.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(improve): live traffic canary with a forward trial (opt-in)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(improve): hold the canary cap across processes, count sessions, and close the review gaps

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(improve): reuse the monitor's finishedRuns, significantDrop and judgeApplied in the canary trial

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(improve): a replay canary-end from healOnce does not end a running traffic canary

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(improve): wire the traffic canary into healOnce and the watcher

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(improve): the traffic canary in the loop, settings, notifications and CLI

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(improve): in manual mode the traffic canary starts only on approve

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: regenerate llms-full.txt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…#1014)

With S12 on main, loop scenario canary-traffic opts in to the live
traffic canary (share 0.25, 5 sessions), starts a canary whose candidate
makes runs read a missing file, drives fresh sessions through
harnessCanary until the forward trial decides, and settles it. It must
be retired, not promoted, with at least one new failure class. Rows for
it can be appended to an existing live proof file: the report then
counts them with the rest.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(studio): C17 elkjs layout worker

Replace dagre auto-layout with elkjs (layered, RIGHT) running in a web
worker (canvas/layout.worker.ts), with an in-thread elk.bundled.js
fallback where Worker is unavailable (vitest/jsdom). toElkGraph nests
expanded inline-agent groups as ELK children (collapsed groups stay
leaves), applyElkPositions maps ELK's relative child positions straight
onto React Flow sub-flow coordinates, and autoLayout supports scope
'all' | 'unplaced' with a 5s timeout on a lazily created, page-wide
worker.

CanvasArea: the Auto layout button awaits the worker and shows
aria-busy; a new useLayoutUnplaced hook lays out graph.unplaced once per
agent id after every node is measured, writing with setGraph(...,
{ dirty: false }) - a new option on useAgentDocument/AppState.setGraph -
and toasts "Couldn't lay out the canvas." on failure.

Notes participate in the layout but never move; dagre and @types/dagre
are removed, elkjs@0.9.3 added; layout.worker.ts is a fallow entry.

* fix(studio): C17 review - unplaced layout gate, cancel scope, stale-write merge

- Measurement gate: nodes hidden inside a collapsed group are never
  rendered/measured, so allVisibleMeasured (new, exported from
  CanvasArea) exempts collapsedHiddenIds - fixes useLayoutUnplaced never
  firing on agents with collapsed groups, and the same pre-existing flaw
  in useFitViewOnLoad.
- Cancellation scope: useLayoutUnplaced no longer cancels the in-flight
  elkjs request on every dep change (measured batches, edits); only an
  agent switch or unmount cancels, via a [agentId] cleanup effect. The
  once-per-agent marker is kept on success so unplaceable ids (hidden
  under collapsed groups) cannot retrigger the layout forever; failures
  clear it for a retry on the next change.
- Stale writes: new applyLayoutResult(before, laidOut, latest) merges
  only actually-moved positions onto the LATEST graph inside the
  setGraph updater - concurrent edits and node data are preserved, a
  node the user moved mid-layout keeps its position, and unplaced loses
  only the moved ids. Both auto-layout ('all') and useLayoutUnplaced
  ('unplaced') use it; auto-layout additionally drops the result when
  the agent switched mid-flight (latestAgentId ref) and skips setGraph
  entirely on a no-op layout. Auto-layout keeps the default dirty flag
  so positions persist via autosave.

Tests: applyLayoutResult merge/no-op cases in layout.test.ts;
canvasMeasured.test.ts for the collapsed-group gate; useLayoutUnplaced
.test.tsx covering the measured gate, collapsed-hidden exemption,
mid-flight dep changes, concurrent-edit merge, agent-switch drop, and
failure toast + retry.

* fix(studio): C17 review - StrictMode remount cancels unplaced layout, hidden-unplaced retry
* feat(studio): C27 agent run settings inspector

Replace the agent node's Advanced JSON editor with real forms for every
remaining AGENT_FIELD_KEYS entry: Limits (maxSteps + RunLimits),
Approvals (permissionMode, approvalTtlMs, askQuestion), Reliability
(retry + rateLimit), Context (compaction, promptCaching, toolSearch,
codeMode) and Delegation (subagentOptions, maxSubagentDepth, maxHandoffs,
root agent only). Nested spec objects merge via mergeNested and are
deleted when emptied; retry:false/compaction:false stay distinct from
unset.

SDK shapes win over the ticket table where they differ: retry.backoff is
a BackoffOptions object (Exponential/Linear write {factor:2}/{factor:1}
presets), promptCaching is 'auto'|false, toolSearch/codeMode have no
enabled flag, and subagentOptions edits the real
{maxConcurrent,awaitBackgroundOnFinish,background,continuation} fields
instead of PLAN's parallel/readOnly.

* fix(studio): C27 review — toolSearch off writes false, approval ttl ceiling, backoff preserves sub-keys

- toolSearch: only the literal false disables it (absent/{} are on), so
  the switch now reads fields.toolSearch !== false and writes false for
  off / the existing object or {} for on — deleting the key used to
  leave tool search enabled.
- approvalTtlMs: clamp the Inspector to the SDK's MAX_APPROVAL_TTL_MS
  (re-exported through graph/v2/specTypes) with an 'At most 100 years'
  message.
- retry.backoff presets now merge their factor over the stored
  BackoffOptions, so spec-file sub-keys (initialMs, maxMs, jitter)
  survive a re-pick instead of being discarded.
Ctrl/Cmd+J, the nav trigger, the agents list's Describe with AI, the
empty canvas, the command palette and the agent header's Change with AI
all funnel through a shared openAuthor store; the docked panel consumes
each pending request once (focus, optional new chat, empty-composer draft
- never sent - and composer focus). Route changes keep the composer focus
chip on the agent on screen until the user clears it for that chat.
…l caps (#1015)

* docs(improve): record the live self-healing proof (S30)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ci): raise the tarball size caps after the elkjs Studio layout

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ci): raise the size-check tarball budget to the measured size + 5%

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ues (#1017)

Add bug report and feature request issue forms, turn off blank issues and
point questions to Discussions. Add the Contributor Covenant 2.1 with
private reporting through the repository's advisory form, and link it from
CONTRIBUTING.md. Add a working list of good first issues under .github/,
outside docs/, so it does not ship in the npm package or llms.txt.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ra, LangGraph.js) (#1020)

Competitor cells come from each project's official docs, checked 9 Nov 2026.
Every partial and missing cell has a footnote to the page read. Regenerates
llms.txt and llms-full.txt.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…#1022)

scripts/repo-metadata.sh holds the gh repo edit commands for the
description, homepage, Discussions and topics; it is not run here.
.github/assets/social-preview.png is the 1280x640 preview to upload.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Scaffold with create-lousho, replay an eval offline, edit the prompt,
and watch the replay flag the changed request. The cassette is a real
recording against a local Ollama model; no API key is used.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…-it path (#1019)

* docs(readme): lead with offline evals, hero studio shot, try-it path

Rewrite the README around what Lousho does differently: evals that
replay offline, crash resume, approvals across processes, and running
in your own Node process. Add a hero screenshot of Agent Forge, a
verified no-key try-it path, a short comparison table that links to
docs/comparison.md, a status note and a community section. Regenerate
llms.txt and llms-full.txt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(readme): shrink logo and hero so the pitch and why sit higher

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…PR (#1018)

* ci(release): release weekly and on demand instead of on every merged PR

The Release workflow now runs on workflow_dispatch and every Monday at
06:00 UTC. A plan job skips the scheduled run when nothing but release
commits or norelease PRs landed since the last tag. GitHub Release notes
group the PRs by conventional-commit type (feat, fix, docs, other).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(release): keep changesSince module-private

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* docs(readme): show the offline eval demo GIF

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(readme): point questions to Discussions

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: regenerate llms-full.txt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The repository was renamed from agent-sdk to lousho. CHANGELOG.md keeps
the old URLs as history; llms.txt and llms-full.txt are regenerated.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…entry (#1026)

* feat(workspaces): workspace.json v1 schema and the lousho/workspaces entry

Adds workspaceManifestSchema (lousho.workspace/1) with strict objects
everywhere, the WorkspaceManifest and WorkspaceLimits types, and the new
lousho/workspaces subpath with its build entry and API report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(workspaces): pin the manifest schema to its types and share contract helpers

Adds workspace.test-d.ts asserting the inferred manifest schema equals
WorkspaceManifest (and WorkspaceLimits) in both directions, and moves the
kebab-case pattern and workspace path schema into contracts/common.ts for
the sibling contracts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(workspaces): harness contract and its forbidden keys

Add harnessSchema and harnessProfileSchema (lousho.harness/1) with
hasExecutableCriterion, and a forbidden-key scanner that rejects the R13
security keys and the R18 execution keys at any depth.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(workspaces): harden the harness forbidden-key scan

Cap the scan depth and reject non-plain data (inherited properties,
getters, Maps, class instances) as issues instead of throwing or
skipping them. Exempt JSON Schema name-map entries only when their value
is a schema, add dependentRequired, reuse refineUniqueIds, state the
base folder for harness paths, and drop zod-3-only message assertions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(workspaces): validate the scanned copy of a harness

The forbidden-key scan now builds a plain-data copy from the values it
read, and the harness schemas validate that copy, so a Proxy cannot show
the scan one thing and zod another. A throwing trap becomes an issue.
The raw scanner is module-private; checkForbiddenKeys and
forbiddenKeyErrors take key rules and always report unscannable values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
LinuxDevil and others added 26 commits November 14, 2026 16:18
test(studio): X08 scripted-Author Playwright smoke for the forge flow
# Conflicts:
#	apps/agent-forge/playwright.config.ts
feat(studio): X05 deploy builds the published version
# Conflicts:
#	apps/agent-forge/server/__tests__/channelTriggers.test.ts
#	apps/agent-forge/server/__tests__/hooksRoute.test.ts
#	apps/agent-forge/server/__tests__/workspaceSeam.test.ts
#	apps/agent-forge/server/app.ts
#	apps/agent-forge/server/triggers/channelTriggers.ts
#	apps/agent-forge/server/workspace/__tests__/middleware.test.ts
#	apps/agent-forge/server/workspace/context.ts
#	apps/agent-forge/shared/wireTypes.ts
#	apps/agent-forge/src/canvas/NodeCard.tsx
#	apps/agent-forge/src/components/CanvasArea.tsx
#	apps/agent-forge/src/screens/agentDetail/AgentOverviewTab.tsx
#	apps/agent-forge/src/screens/agentDetail/__tests__/AgentOverviewTab.test.tsx
#	apps/agent-forge/src/screens/agentDetail/agentOverview.css
…gate

# Conflicts:
#	apps/agent-forge/server/app.ts
#	apps/agent-forge/server/evals/gateRunner.ts
#	apps/agent-forge/server/lifecycle/publish.ts
#	apps/agent-forge/src/graph/v2/specTypes.ts
#	src/spec/v2/evals.ts
#	src/spec/v2/index.ts
# Conflicts:
#	apps/agent-forge/server/app.ts
#	apps/agent-forge/src/runtime/__tests__/runtimeClient.test.ts
feat(studio): W03a Studio home ~/.lousho/studio + one-time legacy import
feat(studio): R09 trigger host respects meta.enabled
# Conflicts:
#	apps/agent-forge/server/app.ts
#	apps/agent-forge/src/runtime/__tests__/runtimeClient.test.ts
feat(studio): X01b run the eval gate on publish and show failures
feat(studio): X02b serve card on the Code tab
# Conflicts:
#	apps/agent-forge/server/index.ts
feat(studio): X04a <forge-chat> embeddable chat widget element
feat(studio): X03 serve the published agent as an MCP server
docs: X07a builder and Agent Author pages
feat(studio): X04b bundle, serve and embed the chat widget
docs: X07b lifecycle, triggers, publish, serve and deploy
# Conflicts:
#	.fallowrc.json
#	apps/agent-forge/package.json
#	apps/agent-forge/server/__tests__/deployRunner.test.ts
#	apps/agent-forge/server/index.ts
#	apps/agent-forge/server/lifecycle/publish.ts
#	apps/agent-forge/server/serve/serveRoutes.ts
…cher

The @linuxdevil/lousho package no longer contains apps/agent-forge:
files drops the Studio entries, prepublishOnly is just the SDK build,
prepack-check loses its Agent-Forge warning, and pack-smoke no longer
builds Studio, requires its paths, or allows anything under apps/.
Re-measured budgets: 1016 entries / 15,289,389 B unpacked / 4,393,121 B
packed (~10% headroom); MAX_ENTRIES stays at 1040 since the count still
fits. size-budget.json regenerated (bundleBytes also dropped ~30% vs
the stale S16b budget - drift, not this change).

src/cli/studio.ts is rewritten around findStudio(): the SDK checkout's
apps/agent-forge (package name @linuxdevil/studio) keeps the dev/prod
contributor modes - prod now spawns dist-server/cli.cjs with
--workspace/--port/--host and lets the Studio CLI mint its own token -
while an installed @linuxdevil/studio is found via createRequire from
cwd, then the SDK package root, then npm's global root, and launched
the same way. --prod/--dev against an installed target is a usage
error (exit 2); no target prints 'Studio is a separate install: npx
@linuxdevil/studio' and exits 1. spawn is injectable for tests.

Verified: tsc, lint, test:coverage (7427), build, pack-smoke (all
checks incl. strict-libs zod3/zod4), size-check, fallow (no new
findings), npm pack --dry-run shows zero apps/ paths, live
'lousho studio --help' and the missing-Studio exit-1 path.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
scripts/pack-smoke-studio.ts packs the SDK and @linuxdevil/studio,
checks the Studio tarball (required/forbidden paths, budgets, pinned
semver SDK dep, package.json restored by prepack/postpack), dry-run
publishes it, installs both tarballs into a fresh project, and proves
`lousho-studio` and the `lousho studio` launcher serve the UI.

smokeKit gains shared dryRunPublish/freePort; pack-smoke.ts uses them.
ci.yml moves all Studio checks out of the check/forge jobs into a
dedicated studio job that ends with pack-smoke:studio --skip-build.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ocumented deep seams

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- host banners and test fixtures now name @linuxdevil/lousho (the
  @lousho/build-ai-agent strings predated the W13a rename; the legacy
  name stays supported in workspaceSdk discovery only).
- specTypes stops re-exporting SecretRef so shared/wireTypes stays the
  single Studio-side export (fallow duplicate-exports); the three canvas
  consumers and types.test.ts import it from wireTypes, the same source
  the other fields already use.
- pack-smoke and pack-smoke-studio share packTarball/smokeDirs/
  smokeSetup/checkTarballContents/cleanSmokeDir/reportSmoke via
  smokeKit (fallow clone groups); mustRun goes private now that
  smokeSetup wraps it.
- drop unused dagre devDependency (elkjs replaced it in C17).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@LinuxDevil

Copy link
Copy Markdown
Owner Author

Consolidated into #1155 — the full W-chain + A08b stack now merges as one PR to run a single CI cycle. This branch's commits are included there.

@LinuxDevil LinuxDevil closed this Nov 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant