@andespindola/brainlink 0.1.0-beta.49 → 0.1.0-beta.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -406,6 +406,7 @@ blink agent upgrade
406
406
  ```
407
407
 
408
408
  This configures `~/.codex/config.toml` with Brainlink MCP (`brainlink-mcp`) so Brainlink is available by default in agent sessions.
409
+ `agent install` and `agent upgrade` also apply the MCP `fully-auto` bootstrap policy by default (`enforceBootstrap`, `enforceContextFirst`, `autoBootstrapOnRead`, `autoBootstrapOnStartup` all enabled).
409
410
 
410
411
  If you are inside this repository and want plugin gallery setup too:
411
412
 
@@ -17,6 +17,7 @@ const worldCoordinateLimit = 5_000_000
17
17
  const transformCoordinateLimit = 20_000_000
18
18
  const hoverHitTestIntervalMs = 64
19
19
  const overviewClusterMaxCount = 1400
20
+ const zoomRecoveryGuardMs = 560
20
21
  const state = {
21
22
  graph: { nodes: [], edges: [] },
22
23
  nodes: [],
@@ -52,7 +53,8 @@ const state = {
52
53
  macroRepresentative: null,
53
54
  filterWorker: null,
54
55
  filterReady: false,
55
- lastHoverHitAt: 0
56
+ lastHoverHitAt: 0,
57
+ lastManualZoomAt: 0
56
58
  }
57
59
 
58
60
  const byId = id => document.getElementById(id)
@@ -1195,7 +1197,8 @@ const render = now => {
1195
1197
  computeRenderVisibility()
1196
1198
  tick(delta)
1197
1199
  const hasVisibleNodeOnScreen = state.renderNodes.some((node) => isNodeVisibleOnScreen(node, width, height))
1198
- if (!hasVisibleNodeOnScreen && state.renderNodes.length > 0) {
1200
+ const manualZoomGuardActive = now - state.lastManualZoomAt < zoomRecoveryGuardMs
1201
+ if (!hasVisibleNodeOnScreen && state.renderNodes.length > 0 && !manualZoomGuardActive) {
1199
1202
  state.offscreenFrameCount += 1
1200
1203
  if (state.offscreenFrameCount >= 6 && !state.recoveringViewport) {
1201
1204
  state.recoveringViewport = true
@@ -1395,7 +1398,7 @@ const selectNodeById = id => {
1395
1398
  if (node) selectNode(node, { openContent: true })
1396
1399
  }
1397
1400
 
1398
- const zoomAtPoint = (screenX, screenY, factor) => {
1401
+ const zoomAtPoint = (screenX, screenY, factor, source = 'generic') => {
1399
1402
  const nextScale = clampScale(state.transform.scale * factor)
1400
1403
  if (nextScale === state.transform.scale) return
1401
1404
  const worldX = (screenX - state.transform.x) / state.transform.scale
@@ -1404,6 +1407,9 @@ const zoomAtPoint = (screenX, screenY, factor) => {
1404
1407
  state.transform.x = clampTransformCoordinate(screenX - worldX * nextScale)
1405
1408
  state.transform.y = clampTransformCoordinate(screenY - worldY * nextScale)
1406
1409
  state.offscreenFrameCount = 0
1410
+ if (source === 'wheel') {
1411
+ state.lastManualZoomAt = performance.now()
1412
+ }
1407
1413
  markRenderDirty()
1408
1414
  }
1409
1415
 
@@ -1422,32 +1428,24 @@ const wheelZoomFactor = event => {
1422
1428
  return event.deltaY < 0 ? 1 + adjustedStep : 1 / (1 + adjustedStep)
1423
1429
  }
1424
1430
 
1425
- const isScreenPointInsideCanvas = (screenX, screenY) => {
1426
- const rect = canvas.getBoundingClientRect()
1427
-
1428
- return screenX >= rect.left && screenX <= rect.right && screenY >= rect.top && screenY <= rect.bottom
1429
- }
1430
-
1431
1431
  const handleWheelZoom = event => {
1432
1432
  if (elements.contentDialog?.open) {
1433
1433
  return
1434
1434
  }
1435
1435
 
1436
- if (!isScreenPointInsideCanvas(event.clientX, event.clientY)) {
1437
- return
1438
- }
1439
-
1440
1436
  event.preventDefault()
1441
1437
  const rect = canvas.getBoundingClientRect()
1442
- const cursorX = event.clientX - rect.left
1443
- const cursorY = event.clientY - rect.top
1438
+ const rawCursorX = Number.isFinite(event.offsetX) ? event.offsetX : event.clientX - rect.left
1439
+ const rawCursorY = Number.isFinite(event.offsetY) ? event.offsetY : event.clientY - rect.top
1440
+ const cursorX = Math.max(0, Math.min(Math.max(rect.width, 320), rawCursorX))
1441
+ const cursorY = Math.max(0, Math.min(Math.max(rect.height, 320), rawCursorY))
1444
1442
  const factor = wheelZoomFactor(event)
1445
1443
 
1446
1444
  if (!Number.isFinite(factor) || factor <= 0 || factor === 1) {
1447
1445
  return
1448
1446
  }
1449
1447
 
1450
- zoomAtPoint(cursorX, cursorY, factor)
1448
+ zoomAtPoint(cursorX, cursorY, factor, 'wheel')
1451
1449
  }
1452
1450
 
1453
1451
  const bindEvents = () => {
@@ -1493,7 +1491,7 @@ const bindEvents = () => {
1493
1491
  }
1494
1492
  if (event.target === elements.contentDialog) elements.contentDialog.close()
1495
1493
  })
1496
- window.addEventListener('wheel', handleWheelZoom, { passive: false })
1494
+ canvas.addEventListener('wheel', handleWheelZoom, { passive: false })
1497
1495
  canvas.addEventListener('dblclick', event => {
1498
1496
  const rect = canvas.getBoundingClientRect()
1499
1497
  const cursorX = event.clientX - rect.left
@@ -141,6 +141,12 @@ const parseAllowedVaults = (value) => {
141
141
  export const installAgentIntegration = async (input) => {
142
142
  const codexConfigPath = getCodexConfigPath();
143
143
  const allowedVaults = parseAllowedVaults(input.allowedVaults);
144
+ const bootstrapPolicy = await setBootstrapPolicy({
145
+ enforceBootstrap: true,
146
+ enforceContextFirst: true,
147
+ autoBootstrapOnRead: true,
148
+ autoBootstrapOnStartup: true
149
+ });
144
150
  await upsertCodexMcpConfig(codexConfigPath, {
145
151
  allowedVaults,
146
152
  brainlinkHome: input.brainlinkHome
@@ -195,6 +201,7 @@ export const installAgentIntegration = async (input) => {
195
201
  codexConfigPath,
196
202
  mcpServer: 'brainlink',
197
203
  command: 'brainlink-mcp',
204
+ bootstrapPolicy,
198
205
  ...(input.mcpOnly !== true ? { pluginSourcePath, pluginSymlinkPath, marketplacePath } : {}),
199
206
  ...(selfTestResult ? { selfTest: selfTestResult } : {}),
200
207
  ...(warnings.length > 0 ? { warnings } : {})
@@ -404,6 +404,7 @@ blink agent status
404
404
  ```
405
405
 
406
406
  `agent install` configures Brainlink MCP in `~/.codex/config.toml` so compatible agents can use Brainlink by default.
407
+ `agent install` and `agent upgrade` automatically apply the `fully-auto` MCP bootstrap policy (`enforceBootstrap=true`, `enforceContextFirst=true`, `autoBootstrapOnRead=true`, `autoBootstrapOnStartup=true`) so all plug-and-play Brainlink features start enabled.
407
408
  Use `agent upgrade` on legacy installations to reapply the latest defaults and run self-test diagnostics.
408
409
  Use `agent policy --preset fully-auto` to keep startup/read auto-bootstrap enabled, or `agent policy --preset strict` to force explicit bootstrap calls.
409
410
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andespindola/brainlink",
3
- "version": "0.1.0-beta.49",
3
+ "version": "0.1.0-beta.50",
4
4
  "description": "Local-first knowledge memory for agents with Markdown, backlinks, indexing and context retrieval.",
5
5
  "type": "module",
6
6
  "license": "MIT",