diff --git a/PRIVACY.md b/PRIVACY.md
index b4b18bcd..de6210e8 100644
--- a/PRIVACY.md
+++ b/PRIVACY.md
@@ -10,9 +10,9 @@ Trace artifacts, cache files, plugins, user adapters, and site memory are stored
## Local site-memory seed lookup
-`WEBCMD_GLOBAL_MEMORY_URL` enables a public unauthenticated GET `/v1/site-memory/seeds/` on first access when no local memory exists. The request uses a 2-second timeout and no retry. It discloses the resolved product/domain.
+Webcmd defaults to a public unauthenticated GET `https://api.webcmd.dev/v1/site-memory/seeds/` on first access when no local product memory exists. The request discloses only the resolved product/domain; it sends no credentials, page contents, local memory, or candidate evidence. `WEBCMD_GLOBAL_MEMORY=off` disables the request. `WEBCMD_GLOBAL_MEMORY_URL` is only a developer/test override.
-With no URL configured, learning is local-only and Webcmd makes no seed request. `WEBCMD_GLOBAL_MEMORY=off` disables even a configured URL.
+The lookup uses a 2-second timeout and no retry, and never refreshes initialized memory.
## Candidate public-IP provenance
diff --git a/src/site-memory/seed-client.test.ts b/src/site-memory/seed-client.test.ts
index ab3ae9db..5dad5ead 100644
--- a/src/site-memory/seed-client.test.ts
+++ b/src/site-memory/seed-client.test.ts
@@ -5,12 +5,15 @@ const urlEnv = { WEBCMD_GLOBAL_MEMORY_URL: 'https://api.webcmd.dev' };
describe('global seed client', () => {
it.each([{}, { WEBCMD_GLOBAL_MEMORY_URL: ' ' }])(
- 'does not fetch without a configured remote URL',
+ 'uses the official default URL without a non-empty override',
async (env) => {
- const fetch = vi.fn();
+ const fetch = vi.fn(async (input: RequestInfo | URL) => {
+ expect(String(input)).toBe('https://api.webcmd.dev/v1/site-memory/seeds/example.test');
+ return jsonResponse({ revision: 'seed-1', site: '# Example\n' });
+ });
await expect(createHttpSeedProvider({ fetch, env }).lookup('example.test'))
- .resolves.toEqual({ status: 'unattempted' });
- expect(fetch).not.toHaveBeenCalled();
+ .resolves.toEqual({ status: 'available', revision: 'seed-1', site: '# Example\n' });
+ expect(fetch).toHaveBeenCalledTimes(1);
},
);
@@ -78,7 +81,10 @@ describe('global seed client', () => {
expect(result).toEqual({ status: 'lookup-failed' });
expect(calls).toBe(1);
- expect(Date.now() - started).toBeGreaterThanOrEqual(2000);
+ // A few ms of scheduler slack under CI is expected: AbortSignal.timeout's
+ // internal timer can fire a hair before the full duration has elapsed
+ // relative to Date.now()'s millisecond sampling.
+ expect(Date.now() - started).toBeGreaterThanOrEqual(1990);
expect(Date.now() - started).toBeLessThan(4000);
});
diff --git a/src/site-memory/seed-client.ts b/src/site-memory/seed-client.ts
index adeb059e..b84aa910 100644
--- a/src/site-memory/seed-client.ts
+++ b/src/site-memory/seed-client.ts
@@ -5,6 +5,7 @@ export interface GlobalSeedProvider {
}
const LOOKUP_TIMEOUT_MS = 2000;
+export const DEFAULT_GLOBAL_MEMORY_URL = 'https://api.webcmd.dev';
export function createHttpSeedProvider(options: {
fetch?: typeof fetch;
@@ -15,8 +16,8 @@ export function createHttpSeedProvider(options: {
return {
async lookup(productKey, signal) {
- const baseUrl = env.WEBCMD_GLOBAL_MEMORY_URL?.trim();
- if (env.WEBCMD_GLOBAL_MEMORY === 'off' || !baseUrl) return { status: 'unattempted' };
+ if (env.WEBCMD_GLOBAL_MEMORY === 'off') return { status: 'unattempted' };
+ const baseUrl = env.WEBCMD_GLOBAL_MEMORY_URL?.trim() || DEFAULT_GLOBAL_MEMORY_URL;
const base = baseUrl.replace(/\/+$/, '');
const url = `${base}/v1/site-memory/seeds/${encodeURIComponent(productKey)}`;
diff --git a/src/site-memory/self-learning.integration.test.ts b/src/site-memory/self-learning.integration.test.ts
index 904908b5..69c4edc4 100644
--- a/src/site-memory/self-learning.integration.test.ts
+++ b/src/site-memory/self-learning.integration.test.ts
@@ -75,7 +75,7 @@ describe('self-learning lifecycle', () => {
url: 'https://local.test/',
taskId: 'task-local',
homeDir,
- seedProvider: createHttpSeedProvider({ fetch: localFetch, env: {} }),
+ seedProvider: createHttpSeedProvider({ fetch: localFetch, env: { WEBCMD_GLOBAL_MEMORY: 'off' } }),
});
expect(seeded.manifest?.seed).toEqual({ status: 'available', revision: 'seed-1' });
diff --git a/src/skills.test.ts b/src/skills.test.ts
index 89e29307..f54c6b1a 100644
--- a/src/skills.test.ts
+++ b/src/skills.test.ts
@@ -542,10 +542,10 @@ describe('public copy', () => {
expect(intro).toMatch(/except|seed lookup/i);
expect(privacy).toMatch(/unauthenticated GET/i);
expect(privacy).toContain('/v1/site-memory/seeds/');
- expect(privacy).not.toMatch(/default(?: base)?(?: is|:)[^\n]*api\.webcmd\.dev/i);
- expect(privacy).toMatch(/WEBCMD_GLOBAL_MEMORY_URL[^\n]*(?:enables|configured)/i);
- expect(privacy).toMatch(/absent|no URL|not configured|without a(?:n)?(?: configured)? URL/i);
- expect(privacy).toMatch(/local-only|no request|does not (?:make|send|perform) (?:a |the )?seed/i);
+ expect(privacy).toMatch(/defaults?[^\n]*api\.webcmd\.dev/i);
+ expect(privacy).toMatch(/WEBCMD_GLOBAL_MEMORY_URL[^\n]*(?:developer|test)[^\n]*override/i);
+ expect(privacy).toMatch(/no local product memory/i);
+ expect(privacy).toMatch(/never refresh(?:es)? initialized memory/i);
expect(privacy).toMatch(/2-second timeout|2 second timeout/i);
expect(privacy).toMatch(/no retry/i);
expect(privacy).toContain('WEBCMD_GLOBAL_MEMORY=off');