@gmickel/gno 2.12.1 → 2.12.2

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
@@ -139,7 +139,7 @@ See the [guide](docs/COMPILED-CONTEXT.md).
139
139
 
140
140
  <!-- public-truth:current-version -->
141
141
 
142
- > Current source version: **v2.12.1**. See [CHANGELOG.md](./CHANGELOG.md).
142
+ > Current source version: **v2.12.2**. See [CHANGELOG.md](./CHANGELOG.md).
143
143
 
144
144
  <!-- /public-truth -->
145
145
 
@@ -0,0 +1 @@
1
+ 34953d40a06904ec1c0fc7c6df54aaa14cb24acb05b38c3f15d572a285f7b3fc gno-browser-clipper-v2.12.2.zip
@@ -21,5 +21,5 @@
21
21
  "content_security_policy": {
22
22
  "extension_pages": "script-src 'self'; object-src 'none'; connect-src http://127.0.0.1:*"
23
23
  },
24
- "version": "2.12.1"
24
+ "version": "2.12.2"
25
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gmickel/gno",
3
- "version": "2.12.1",
3
+ "version": "2.12.2",
4
4
  "description": "Local semantic search for your documents. Index Markdown, PDF, and Office files with hybrid BM25 + vector search.",
5
5
  "keywords": [
6
6
  "embeddings",
package/spec/mcp.md CHANGED
@@ -3105,7 +3105,7 @@ Clients should check schema version for compatibility.
3105
3105
  - DB connection kept open for server lifetime
3106
3106
  - No persistent state between tool calls
3107
3107
  - Each tool call is independent
3108
- - Server handles concurrent requests sequentially
3108
+ - Server handles concurrent tool calls sequentially; an accepted background job (`gno_sync`, `gno_embed`, `gno_index`) waits for the call in progress, then runs without holding that queue, so reads are not blocked while it runs
3109
3109
 
3110
3110
  ---
3111
3111
 
@@ -287,20 +287,12 @@ export class JobManager {
287
287
  lock: { release: () => Promise<void> }
288
288
  ): Promise<void> {
289
289
  try {
290
- const release = await this.#toolMutex.acquire();
291
- try {
292
- this.#assertAuthorizationEpoch(job);
293
- assertInferenceActive();
294
- const result = await fn(this.#jobControllers.get(job.id)!.signal);
295
- assertInferenceActive();
296
- job.status = "completed";
297
- job.result = result;
298
- } catch (e) {
299
- job.status = "failed";
300
- job.error = e instanceof Error ? e.message : String(e);
301
- } finally {
302
- release();
303
- }
290
+ await this.#admit(job);
291
+ assertInferenceActive();
292
+ const result = await fn(this.#jobControllers.get(job.id)!.signal);
293
+ assertInferenceActive();
294
+ job.status = "completed";
295
+ job.result = result;
304
296
  } catch (e) {
305
297
  job.status = "failed";
306
298
  job.error = e instanceof Error ? e.message : String(e);
@@ -378,20 +370,12 @@ export class JobManager {
378
370
  lock: { release: () => Promise<void> }
379
371
  ): Promise<void> {
380
372
  try {
381
- const release = await this.#toolMutex.acquire();
382
- try {
383
- this.#assertAuthorizationEpoch(job);
384
- assertInferenceActive();
385
- const result = await fn(this.#jobControllers.get(job.id)!.signal);
386
- assertInferenceActive();
387
- job.status = "completed";
388
- job.typedResult = result;
389
- } catch (e) {
390
- job.status = "failed";
391
- job.error = e instanceof Error ? e.message : String(e);
392
- } finally {
393
- release();
394
- }
373
+ await this.#admit(job);
374
+ assertInferenceActive();
375
+ const result = await fn(this.#jobControllers.get(job.id)!.signal);
376
+ assertInferenceActive();
377
+ job.status = "completed";
378
+ job.typedResult = result;
395
379
  } catch (e) {
396
380
  job.status = "failed";
397
381
  job.error = e instanceof Error ? e.message : String(e);
@@ -435,6 +419,20 @@ export class JobManager {
435
419
  }
436
420
  }
437
421
 
422
+ /**
423
+ * Wait for the tool call in progress, then re-check authorization. The
424
+ * mutex is released before the job's work runs, so reads are not held
425
+ * behind a long sync or embed; writers stay serialized by the write lease.
426
+ */
427
+ async #admit(job: JobRecord): Promise<void> {
428
+ const release = await this.#toolMutex.acquire();
429
+ try {
430
+ this.#assertAuthorizationEpoch(job);
431
+ } finally {
432
+ release();
433
+ }
434
+ }
435
+
438
436
  #assertAuthorizationEpoch(job: JobRecord): void {
439
437
  if (this.#jobAuthorizationEpochs.get(job.id) === this.#authorizationEpoch) {
440
438
  return;
@@ -1 +0,0 @@
1
- 0b2bb11d4bf3e827e847426464808224e803bc7a84a4e065541a7d057f5cb651 gno-browser-clipper-v2.12.1.zip