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
33 changes: 33 additions & 0 deletions packages/workspace-lens/__tests__/workspace-lens.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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] }];
Expand Down
7 changes: 5 additions & 2 deletions packages/workspace-lens/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -11,7 +11,10 @@
"import": "./dist/index.js"
}
},
"files": ["dist", "README.md"],
"files": [
"dist",
"README.md"
],
"publishConfig": {
"access": "public"
},
Expand Down
29 changes: 29 additions & 0 deletions packages/workspace-lens/src/lens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,35 @@ 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<WorkspaceLens> {
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<WorkspaceLens> {
// 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 });
}

get modelId(): string { return this.artifact.modelId; }
get lensId(): string { return this.artifact.lensId; }
get dModel(): number { return this.artifact.dModel; }
Expand Down
Loading