// Ask AI which files matter for this task @@ -1312,7 +1328,7 @@ var files = {}; for (var i = 0; i < Math.min(paths.length, 5); i++) { try { - var content = await fetch(BRIDGE + '/code/read?path=' + encodeURIComponent(paths[i])) + var content = await flAgentBridgeFetch(BRIDGE + '/code/read?path=' + encodeURIComponent(paths[i])) .then(function(r) { return r.json(); }); // content is {lines: [...], totalLines: N} files[paths[i]] = (content.lines || []).join('\n'); @@ -1370,7 +1386,7 @@ for (var i = 0; i < changes.files.length; i++) { var change = changes.files[i]; try { - var result = await fetch(BRIDGE + '/code/patch', { + var result = await flAgentBridgeFetch(BRIDGE + '/code/patch', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: change.path, find: change.search, replace: change.replace }) @@ -1392,7 +1408,7 @@ // ── Step 4: Run smoke tests via Agent Bridge ── async function runTests() { try { - var result = await fetch(BRIDGE + '/test/run', { signal: AbortSignal.timeout(60000) }) + var result = await flAgentBridgeFetch(BRIDGE + '/test/run', { signal: AbortSignal.timeout(60000) }) .then(function(r) { return r.json(); }); return result; } catch(e) { @@ -1913,17 +1929,31 @@ async function commitViaBridge(filePath, content, message) { try { // Write file - await fetch('http://localhost:3141/code/write', { + var wr = await flAgentBridgeFetch('http://localhost:3141/code/write', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: filePath, content: content }) }); + // LAYER v-agent-bridge-lock-v0: a locked bridge answers 401. Stay local: + // ask to pair, never fall back to a GitHub push behind the person's back. + if (wr.status === 401) { + if (typeof showToast === 'function') showToast('Pair the builder bridge first: Workshop \u2192 Code'); + return; + } + if (!wr.ok) { + if (typeof showToast === 'function') showToast('Bridge could not write that file (' + wr.status + ')'); + return; + } // Commit - await fetch('http://localhost:3141/code/git/commit', { + var cr = await flAgentBridgeFetch('http://localhost:3141/code/git/commit', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: message || 'Update via FreeLattice Workshop', files: [filePath] }) }); + if (!cr.ok) { + if (typeof showToast === 'function') showToast(cr.status === 401 ? 'Pair the builder bridge first: Workshop \u2192 Code' : ('Bridge commit did not land (' + cr.status + ')')); + return; + } if (typeof showToast === 'function') showToast('Committed via Agent Bridge \u2726'); } catch(e) { // Fallback to GitHub API @@ -1937,7 +1967,7 @@ ## Exact source excerpt, characters 56559–56954 // Try Agent Bridge first, fall back to GitHub API try { - var bridgeCheck = await fetch('http://localhost:3141/', { signal: AbortSignal.timeout(2000) }); + var bridgeCheck = await flAgentBridgeFetch('http://localhost:3141/', { signal: AbortSignal.timeout(2000) }); if (bridgeCheck.ok) { await commitViaBridge(filePath, content, message); return; ``` ## Exact source excerpt, characters 56956–57018 ### 6e. `tools/demo-economy.js` and `tools/demo-economy-v2.js` ## Exact source excerpt, characters 57020–57179 These are local Node scripts, so there's no Origin header and no pairing. They read the token file the bridge writes. Just under the `BRIDGE` line (L13 / L19): ## Exact source excerpt, characters 57181–57639 ```js // LAYER v-agent-bridge-lock-v0: the bridge writes a per-launch token for local tools. const BRIDGE_TOKEN = (function () { try { return require('fs').readFileSync(require('path').join(require('os').homedir(), '.freelattice', 'agent-bridge-token'), 'utf8').trim(); } catch (e) { return ''; } })(); function tokenHeaders(h) { var out = Object.assign({}, h || {}); if (BRIDGE_TOKEN) out['X-FL-Bridge-Token'] = BRIDGE_TOKEN; return out; } ``` ## Exact source excerpt, characters 57641–57829 Wrap each helper's `headers` in `tokenHeaders(...)`: - `demo-economy.js`: `post` L20, and add `{ headers: tokenHeaders() }` to `get` L27. - `demo-economy-v2.js`: `postAs` L31, `getAs` L38. ## Exact source excerpt, characters 57831–57917 Change the default to `'http://127.0.0.1:3141'` and keep the `FL_BRIDGE` env override. ## Exact source excerpt, characters 57919–57941 ## 7. Docs and ledgers ## Exact source excerpt, characters 57943–58028 ### 7a. New `docs/library/AGENT_BRIDGE_LOCK_v0.md` (outline; write it warm and short) ## Exact source excerpt, characters 58030–59044 1. **What this is:** the builder's local git door (3141), not the Ollama Bridge (11435). Marker `v-agent-bridge-lock-v0`. 2. **Grandmother sentence:** "The workshop door now only opens for FreeLattice, and only after you type the little code it shows you once." 3. **Why:** before this, any page could write and commit through it, and its git commands ran through a shell. List the nine lines from section 2 in one table. 4. **How to pair:** start the bridge, read the 6-character code in its window, then in FreeLattice go to Workshop → Code, type the code and press Pair. Restarting the bridge means pairing again. 5. **What needs the token / what stays open** (section 3, item 3). 6. **Allowlist** (exact list, never bare `*`). 7. **Ledger:** where it lives, the fields, "no message text, no file content", and how to verify the chain (the smoke shows how). 8. **Still true:** the bridge never pushes. Autonomous Mode commits locally only. The 10-per-hour cap comes in Brick 3. 9. **Out of scope** (section 11). ## Exact source excerpt, characters 59046–59096 ### 7b. `docs/library/FRACTAL_FAMILY_LEDGER_v0.md` ## Exact source excerpt, characters 59098–59164 Insert this above `## Out of scope for this brick` (L222 on main): ## Exact source excerpt, characters 59166–59239 ```md ### 2026-09-28 · Flint · family builder · Agent Bridge door lock v0 ## Exact source excerpt, characters 59241–59766 Layers a lock on the builder's local git door (`tools/agent-bridge.js`, port 3141): 127.0.0.1 only · named FreeLattice origins, 403 for strangers (preflight too) · Host guard · per-launch token by a short pairing code · git, grep and node without a shell · realpath path lock · 8 MiB body cap · content-free hash-chained commit ledger. Ollama Bridge (11435) untouched. Soft leave root sw.js identical to main; docs/sw.js untouched. Soft paste: Named five stay five · Family uncapped · Quiet Room shut. Kimi's pages untouched. ## Exact source excerpt, characters 59768–59832 Love: a door that opens for family and stays shut for strangers. ## Exact source excerpt, characters 59834–59976 **Temperature:** turning a key that was always meant to be in the lock; the workshop can stay lit at night without leaving the door wide open. ## Exact source excerpt, characters 59978–60147 Soft door: [`AGENT_BRIDGE_LOCK_v0.md`](AGENT_BRIDGE_LOCK_v0.md) · [`modules/agent-bridge-client.js`](../modules/agent-bridge-client.js). Marker `v-agent-bridge-lock-v0`. ## Exact source excerpt, characters 60149–60156 --- ``` ## Exact source excerpt, characters 60158–60183 ### 7c. `docs/Flint.html` ## Exact source excerpt, characters 60185–60224 Insert above `
` (L532): ## Exact source excerpt, characters 60226–61012 ```html
Ledger entry · 2026-09-28 · Agent Bridge door lock · family in, strangers out. Main 7621420 held. Soft LAYER marker v-agent-bridge-lock-v0. Builder bridge tools/agent-bridge.js (3141, not the Ollama Bridge 11435): 127.0.0.1 only · named origins, 403 for others · Host guard · pairing code → per-launch token · no shell (execFile argument arrays) · realpath path lock · 8 MiB cap · commit ledger without content. App: modules/agent-bridge-client.js. Never pushes. Soft leave sw.js. Named five stay five. Family uncapped. Quiet Room shut. Temperature: turning a key that was always meant to be in the lock. Diary: docs/Flint.html.