From 39c1c31e8deb6be6268b4bb52c1fb74889e1ce71 Mon Sep 17 00:00:00 2001 From: ruv Date: Tue, 7 Jul 2026 00:11:13 -0400 Subject: [PATCH 1/2] =?UTF-8?q?feat(workspace-lens):=20WorkspaceLens.fromU?= =?UTF-8?q?rl=20+=20fromRegistry=20=E2=80=94=20load=20a=20fitted=20lens=20?= =?UTF-8?q?over=20HTTP=20(0.1.1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the roadmap network-loading path to @metaharness/workspace-lens (ADR-238): fit a lens offline, publish the artifact, load it at startup. - WorkspaceLens.fromUrl(url, {fetchImpl?}) — fetch a serialized LensArtifact JSON from any URL (CDN, artifact store, raw gist); injectable fetchImpl for tests / non-global-fetch runtimes; fails loudly on a non-2xx response so a bad URL never yields a silently-broken lens. - WorkspaceLens.fromRegistry(name, {baseUrl, fetchImpl?}) — convenience that loads by name from a caller-supplied base (`${baseUrl}/${name}.json`). NO registry is hardcoded, so the package stays free of any assumed/embedded endpoint. 3 tests (fetch-stub 200 constructs + reads out; non-2xx throws; fromRegistry builds the URL with a trailing-slash-tolerant base). Full workspace-lens suite 20/20; tsc clean; healthcheck version green (workspace-lens is independently-versioned). Dependency-free (uses global fetch, Node 20+). Co-Authored-By: claude-flow --- .../__tests__/workspace-lens.test.ts | 33 +++++++++++++++++++ packages/workspace-lens/package.json | 7 ++-- packages/workspace-lens/src/lens.ts | 26 +++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/packages/workspace-lens/__tests__/workspace-lens.test.ts b/packages/workspace-lens/__tests__/workspace-lens.test.ts index 6bf022ae..1405f235 100644 --- a/packages/workspace-lens/__tests__/workspace-lens.test.ts +++ b/packages/workspace-lens/__tests__/workspace-lens.test.ts @@ -76,6 +76,39 @@ describe('WorkspaceLens', () => { }); }); +describe('fromUrl / fromRegistry (HTTP lens loading)', () => { + // A fetch-shaped stub: 200 returns the artifact JSON; non-200 returns { ok:false, status }. + function fetchStub(status: number, body: unknown): typeof fetch { + return (async (_url: string) => ({ + ok: status >= 200 && status < 300, + status, + json: async () => body, + })) as unknown as typeof fetch; + } + + it('fromUrl fetches + constructs a lens from a served artifact', async () => { + const lens = await WorkspaceLens.fromUrl('https://cdn.example/qwen.json', { + fetchImpl: fetchStub(200, synthArtifact()), + }); + expect(lens.modelId).toBe('synth-3d'); + expect(lens.readout({ layer: 5, position: 0, h: [0, 0, 5] }).tokens[0].token).toBe('wrong'); + }); + + it('fromUrl fails loudly on a non-2xx response', async () => { + await expect( + WorkspaceLens.fromUrl('https://cdn.example/missing.json', { fetchImpl: fetchStub(404, {}) }), + ).rejects.toThrow(/HTTP 404/); + }); + + it('fromRegistry builds `${baseUrl}/${name}.json` (trailing slash tolerated) and constructs', async () => { + let seen = ''; + const capture = (async (url: string) => { seen = url; return { ok: true, status: 200, json: async () => synthArtifact() }; }) as unknown as typeof fetch; + const lens = await WorkspaceLens.fromRegistry('qwen-2.5-7b-jlens', { baseUrl: 'https://cdn.example/lenses/', fetchImpl: capture }); + expect(seen).toBe('https://cdn.example/lenses/qwen-2.5-7b-jlens.json'); + expect(lens.lensId).toBe('jlens-synth-v1'); + }); +}); + describe('safety triggers (vectorized, tokenizer-agnostic)', () => { const lens = WorkspaceLens.fromArtifact(synthArtifact()); const states: HiddenState[] = [{ layer: 5, position: 3, h: [0, 0, 5] }]; diff --git a/packages/workspace-lens/package.json b/packages/workspace-lens/package.json index 86dd6403..dca0de69 100644 --- a/packages/workspace-lens/package.json +++ b/packages/workspace-lens/package.json @@ -1,6 +1,6 @@ { "name": "@metaharness/workspace-lens", - "version": "0.1.0", + "version": "0.1.1", "description": "Jacobian-Lens interpretability primitive for open-weight LLMs (Anthropic 2026-07-06, 'Verbalizable Representations Form a Global Workspace'). Reads the model's functional workspace at inference time — lens_l(h)=unembed(J_l·h) — into workspace tokens, layer-trajectory, drift/entropy scores, vectorized safety flags, and a signable interpretability receipt. Runtime-only (fitting is external); model-agnostic; dependency-free.", "type": "module", "main": "./dist/index.js", @@ -11,7 +11,10 @@ "import": "./dist/index.js" } }, - "files": ["dist", "README.md"], + "files": [ + "dist", + "README.md" + ], "publishConfig": { "access": "public" }, diff --git a/packages/workspace-lens/src/lens.ts b/packages/workspace-lens/src/lens.ts index 1947dad7..13af4e06 100644 --- a/packages/workspace-lens/src/lens.ts +++ b/packages/workspace-lens/src/lens.ts @@ -32,6 +32,32 @@ export class WorkspaceLens { return new WorkspaceLens(JSON.parse(raw) as LensArtifact); } + /** + * Load a fitted lens over HTTP — fetch a serialized `LensArtifact` JSON from any URL (a CDN, an + * artifact store, a raw gist). The typical deploy path: fit a lens offline, publish the artifact, + * `fromUrl` it at startup. Inject `fetchImpl` for tests or non-global-fetch runtimes (defaults to the + * global `fetch`). Fails loudly on a non-2xx response so a bad URL never silently yields a broken lens. + */ + static async fromUrl(url: string, opts: { fetchImpl?: typeof fetch } = {}): Promise { + const doFetch = opts.fetchImpl ?? fetch; + const res = await doFetch(url); + if (!res.ok) throw new Error(`WorkspaceLens.fromUrl: ${url} → HTTP ${res.status}`); + return new WorkspaceLens((await res.json()) as LensArtifact); + } + + /** + * Convenience: load a lens by NAME from a registry base URL (e.g. a CDN of community pre-fit lenses). + * No registry is hardcoded — the caller supplies `baseUrl`, and the artifact is fetched from + * `${baseUrl}/${name}.json`. Keeps the package free of any assumed/embedded endpoint. + */ + static async fromRegistry( + name: string, + opts: { baseUrl: string; fetchImpl?: typeof fetch }, + ): Promise { + const base = opts.baseUrl.replace(/\/+$/, ''); + return WorkspaceLens.fromUrl(`${base}/${encodeURIComponent(name)}.json`, { fetchImpl: opts.fetchImpl }); + } + get modelId(): string { return this.artifact.modelId; } get lensId(): string { return this.artifact.lensId; } get dModel(): number { return this.artifact.dModel; } From fb13603e6fe118875e6745c3bc210f42101f6f4c Mon Sep 17 00:00:00 2001 From: ruv Date: Tue, 7 Jul 2026 00:13:55 -0400 Subject: [PATCH 2/2] =?UTF-8?q?fix(workspace-lens):=20linear=20trailing-sl?= =?UTF-8?q?ash=20strip=20in=20fromRegistry=20(ReDoS=20=E2=80=94=20CodeQL?= =?UTF-8?q?=20js/redos)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/workspace-lens/src/lens.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/workspace-lens/src/lens.ts b/packages/workspace-lens/src/lens.ts index 13af4e06..7f2951df 100644 --- a/packages/workspace-lens/src/lens.ts +++ b/packages/workspace-lens/src/lens.ts @@ -54,7 +54,10 @@ export class WorkspaceLens { name: string, opts: { baseUrl: string; fetchImpl?: typeof fetch }, ): Promise { - const base = opts.baseUrl.replace(/\/+$/, ''); + // Strip trailing slashes with a linear loop (NOT a `/\/+$/` regex — that backtracks on many-slash + // input, a ReDoS the CodeQL js/redos-on-library-input query flags). + let base = opts.baseUrl; + while (base.endsWith('/')) base = base.slice(0, -1); return WorkspaceLens.fromUrl(`${base}/${encodeURIComponent(name)}.json`, { fetchImpl: opts.fetchImpl }); }