instar 1.3.117 → 1.3.118

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.
@@ -20,6 +20,23 @@ export interface EmbeddingProviderConfig {
20
20
  dimensions?: number;
21
21
  /** Maximum text length to embed in characters (default: 8192) */
22
22
  maxTextLength?: number;
23
+ /**
24
+ * Idle-unload window in ms. After this long with no embed() call, the loaded
25
+ * ONNX pipeline is disposed to free its thread pool, which busy-spins even
26
+ * when idle (measured: ~3.6% of a core on a quiet box, up to ~44% on a
27
+ * contended one — pure waste while an agent isn't doing memory work). The
28
+ * next embed() lazily reloads (~1-3s, verified-identical output). Default
29
+ * 300000 (5 min). Set 0 to disable (keep the model resident forever — the
30
+ * prior behavior).
31
+ */
32
+ idleUnloadMs?: number;
33
+ /**
34
+ * Test/advanced seam: factory that produces the feature-extraction pipeline.
35
+ * Defaults to the real `@huggingface/transformers` import. Tests inject a mock
36
+ * (a callable with a `dispose()`) to exercise the idle-unload lifecycle
37
+ * without loading the 80MB model.
38
+ */
39
+ pipelineFactory?: (modelName: string) => Promise<any>;
23
40
  }
24
41
  export declare class EmbeddingProvider {
25
42
  private pipeline;
@@ -27,7 +44,13 @@ export declare class EmbeddingProvider {
27
44
  private readonly modelName;
28
45
  readonly dimensions: number;
29
46
  private readonly maxTextLength;
47
+ private readonly idleUnloadMs;
48
+ private readonly pipelineFactory?;
30
49
  private vecExtensionLoaded;
50
+ /** In-flight embed() calls — the idle-unload timer never disposes while > 0. */
51
+ private inFlight;
52
+ /** Rolling idle-unload timer (reset on every embed); unref'd so it can't keep the process alive. */
53
+ private idleTimer;
31
54
  constructor(config?: EmbeddingProviderConfig);
32
55
  /**
33
56
  * Ensure the model is loaded. Safe to call multiple times — only loads once.
@@ -38,6 +61,24 @@ export declare class EmbeddingProvider {
38
61
  * Whether the model is loaded and ready for embedding.
39
62
  */
40
63
  get isReady(): boolean;
64
+ /**
65
+ * Arm (or re-arm) the rolling idle-unload timer. Called after every embed so
66
+ * the window restarts on each use — an actively-embedding agent keeps the
67
+ * model resident; one that goes quiet for `idleUnloadMs` unloads it.
68
+ */
69
+ private scheduleIdleUnload;
70
+ /**
71
+ * Dispose the loaded pipeline if it's idle — frees the ONNX thread pool that
72
+ * busy-spins even when no embedding is happening. Guarded on inFlight so a
73
+ * long-running batch that outlasts the window is never disposed mid-flight;
74
+ * its completion re-arms the timer.
75
+ */
76
+ private maybeUnload;
77
+ /**
78
+ * Explicitly release the model + cancel the idle timer (shutdown / tests).
79
+ * Idempotent.
80
+ */
81
+ dispose(): Promise<void>;
41
82
  /**
42
83
  * Generate a normalized embedding for a single text string.
43
84
  * Lazy-initializes the model on first call.
@@ -1 +1 @@
1
- {"version":3,"file":"EmbeddingProvider.d.ts","sourceRoot":"","sources":["../../src/memory/EmbeddingProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,KAAK,QAAQ,GAAG,OAAO,gBAAgB,EAAE,QAAQ,CAAC;AAElD,MAAM,WAAW,uBAAuB;IACtC,oFAAoF;IACpF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,kBAAkB,CAAyB;gBAEvC,MAAM,CAAC,EAAE,uBAAuB;IAQ5C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAWnB,SAAS;IAOvB;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;IAID;;;OAGG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAahD;;;OAGG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IA2B1D;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO;IAgBvC,OAAO,CAAC,gBAAgB,CAA4C;IAEpE;;;;;OAKG;IACG,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IAcvC;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,YAAY,GAAG,MAAM;IAIhD;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY;IAO/C;;;OAGG;IACH,MAAM,CAAC,gBAAgB,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM;CAUlE"}
1
+ {"version":3,"file":"EmbeddingProvider.d.ts","sourceRoot":"","sources":["../../src/memory/EmbeddingProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,KAAK,QAAQ,GAAG,OAAO,gBAAgB,EAAE,QAAQ,CAAC;AAElD,MAAM,WAAW,uBAAuB;IACtC,oFAAoF;IACpF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CACvD;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAsC;IACvE,OAAO,CAAC,kBAAkB,CAAyB;IAEnD,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAAK;IACrB,oGAAoG;IACpG,OAAO,CAAC,SAAS,CAA8C;gBAEnD,MAAM,CAAC,EAAE,uBAAuB;IAU5C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAWnB,SAAS;IAWvB;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAW1B;;;;;OAKG;YACW,WAAW;IAazB;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB9B;;;OAGG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAmBhD;;;OAGG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAmC1D;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO;IAgBvC,OAAO,CAAC,gBAAgB,CAA4C;IAEpE;;;;;OAKG;IACG,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IAcvC;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,YAAY,GAAG,MAAM;IAIhD;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY;IAO/C;;;OAGG;IACH,MAAM,CAAC,gBAAgB,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM;CAUlE"}
@@ -18,11 +18,19 @@ export class EmbeddingProvider {
18
18
  modelName;
19
19
  dimensions;
20
20
  maxTextLength;
21
+ idleUnloadMs;
22
+ pipelineFactory;
21
23
  vecExtensionLoaded = new WeakSet();
24
+ /** In-flight embed() calls — the idle-unload timer never disposes while > 0. */
25
+ inFlight = 0;
26
+ /** Rolling idle-unload timer (reset on every embed); unref'd so it can't keep the process alive. */
27
+ idleTimer = null;
22
28
  constructor(config) {
23
29
  this.modelName = config?.modelName ?? 'Xenova/all-MiniLM-L6-v2';
24
30
  this.dimensions = config?.dimensions ?? 384;
25
31
  this.maxTextLength = config?.maxTextLength ?? 8192;
32
+ this.idleUnloadMs = config?.idleUnloadMs ?? 300_000;
33
+ this.pipelineFactory = config?.pipelineFactory;
26
34
  }
27
35
  // ─── Lifecycle ──────────────────────────────────────────────────
28
36
  /**
@@ -39,6 +47,10 @@ export class EmbeddingProvider {
39
47
  await this.loading;
40
48
  }
41
49
  async loadModel() {
50
+ if (this.pipelineFactory) {
51
+ this.pipeline = await this.pipelineFactory(this.modelName);
52
+ return;
53
+ }
42
54
  const { pipeline: createPipeline } = await import('@huggingface/transformers');
43
55
  this.pipeline = await createPipeline('feature-extraction', this.modelName, {
44
56
  dtype: 'fp32',
@@ -50,20 +62,83 @@ export class EmbeddingProvider {
50
62
  get isReady() {
51
63
  return this.pipeline !== null;
52
64
  }
65
+ /**
66
+ * Arm (or re-arm) the rolling idle-unload timer. Called after every embed so
67
+ * the window restarts on each use — an actively-embedding agent keeps the
68
+ * model resident; one that goes quiet for `idleUnloadMs` unloads it.
69
+ */
70
+ scheduleIdleUnload() {
71
+ if (this.idleUnloadMs <= 0)
72
+ return; // disabled — keep resident
73
+ if (this.idleTimer)
74
+ clearTimeout(this.idleTimer);
75
+ this.idleTimer = setTimeout(() => {
76
+ this.idleTimer = null;
77
+ void this.maybeUnload();
78
+ }, this.idleUnloadMs);
79
+ // Never let the idle timer hold the process open on its own.
80
+ this.idleTimer.unref?.();
81
+ }
82
+ /**
83
+ * Dispose the loaded pipeline if it's idle — frees the ONNX thread pool that
84
+ * busy-spins even when no embedding is happening. Guarded on inFlight so a
85
+ * long-running batch that outlasts the window is never disposed mid-flight;
86
+ * its completion re-arms the timer.
87
+ */
88
+ async maybeUnload() {
89
+ if (this.inFlight > 0 || !this.pipeline)
90
+ return;
91
+ const p = this.pipeline;
92
+ this.pipeline = null;
93
+ this.loading = null;
94
+ try {
95
+ await p?.dispose?.();
96
+ }
97
+ catch {
98
+ // @silent-fallback-ok — dispose is best-effort cleanup; failing to free
99
+ // the session is not fatal (the next embed reloads a fresh pipeline).
100
+ }
101
+ }
102
+ /**
103
+ * Explicitly release the model + cancel the idle timer (shutdown / tests).
104
+ * Idempotent.
105
+ */
106
+ async dispose() {
107
+ if (this.idleTimer) {
108
+ clearTimeout(this.idleTimer);
109
+ this.idleTimer = null;
110
+ }
111
+ if (this.pipeline) {
112
+ const p = this.pipeline;
113
+ this.pipeline = null;
114
+ this.loading = null;
115
+ try {
116
+ await p?.dispose?.();
117
+ }
118
+ catch { /* @silent-fallback-ok — best-effort cleanup */ }
119
+ }
120
+ }
53
121
  // ─── Embedding ──────────────────────────────────────────────────
54
122
  /**
55
123
  * Generate a normalized embedding for a single text string.
56
124
  * Lazy-initializes the model on first call.
57
125
  */
58
126
  async embed(text) {
59
- await this.initialize();
60
- const truncated = text.slice(0, this.maxTextLength);
61
- const output = await this.pipeline(truncated, {
62
- pooling: 'mean',
63
- normalize: true,
64
- });
65
- // output.data is a Float32Array from the ONNX model
66
- return new Float32Array(output.data);
127
+ this.inFlight++;
128
+ try {
129
+ await this.initialize();
130
+ const truncated = text.slice(0, this.maxTextLength);
131
+ const output = await this.pipeline(truncated, {
132
+ pooling: 'mean',
133
+ normalize: true,
134
+ });
135
+ // output.data is a Float32Array from the ONNX model
136
+ return new Float32Array(output.data);
137
+ }
138
+ finally {
139
+ this.inFlight--;
140
+ this.scheduleIdleUnload();
141
+ }
67
142
  }
68
143
  /**
69
144
  * Generate normalized embeddings for multiple texts.
@@ -72,24 +147,33 @@ export class EmbeddingProvider {
72
147
  async embedBatch(texts) {
73
148
  if (texts.length === 0)
74
149
  return [];
150
+ // Single-text delegates to embed(), which owns its own inFlight + idle-timer
151
+ // bookkeeping — don't double-count here.
75
152
  if (texts.length === 1)
76
153
  return [await this.embed(texts[0])];
77
- await this.initialize();
78
- const truncated = texts.map(t => t.slice(0, this.maxTextLength));
79
- const results = [];
80
- // Process in batches of 32 to avoid memory pressure
81
- const batchSize = 32;
82
- for (let i = 0; i < truncated.length; i += batchSize) {
83
- const batch = truncated.slice(i, i + batchSize);
84
- for (const text of batch) {
85
- const output = await this.pipeline(text, {
86
- pooling: 'mean',
87
- normalize: true,
88
- });
89
- results.push(new Float32Array(output.data));
154
+ this.inFlight++;
155
+ try {
156
+ await this.initialize();
157
+ const truncated = texts.map(t => t.slice(0, this.maxTextLength));
158
+ const results = [];
159
+ // Process in batches of 32 to avoid memory pressure
160
+ const batchSize = 32;
161
+ for (let i = 0; i < truncated.length; i += batchSize) {
162
+ const batch = truncated.slice(i, i + batchSize);
163
+ for (const text of batch) {
164
+ const output = await this.pipeline(text, {
165
+ pooling: 'mean',
166
+ normalize: true,
167
+ });
168
+ results.push(new Float32Array(output.data));
169
+ }
90
170
  }
171
+ return results;
172
+ }
173
+ finally {
174
+ this.inFlight--;
175
+ this.scheduleIdleUnload();
91
176
  }
92
- return results;
93
177
  }
94
178
  // ─── sqlite-vec Extension ───────────────────────────────────────
95
179
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"EmbeddingProvider.js","sourceRoot":"","sources":["../../src/memory/EmbeddingProvider.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"EmbeddingProvider.js","sourceRoot":"","sources":["../../src/memory/EmbeddingProvider.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.117",
3
+ "version": "1.3.118",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-05-30T11:35:40.574Z",
5
- "instarVersion": "1.3.117",
4
+ "generatedAt": "2026-05-30T11:54:17.258Z",
5
+ "instarVersion": "1.3.118",
6
6
  "entryCount": 193,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -0,0 +1,51 @@
1
+ # Instar Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+
5
+ ## What Changed
6
+
7
+ **The shared local embedding model now unloads when idle, freeing the CPU its
8
+ ONNX thread pool was busy-spinning on.**
9
+
10
+ `EmbeddingProvider` (backing SemanticMemory / MemoryIndex / TopicMemory) loaded the
11
+ all-MiniLM-L6-v2 ONNX model lazily and then kept it resident forever. onnxruntime's
12
+ thread pool busy-spins even when idle — measured live via `process.cpuUsage()` at
13
+ ~3.6% of a core on a quiet box, and previously ~44% on a contended agent that was
14
+ supposedly paused. With several agents per box each holding an idle, spinning
15
+ model, that's a real multiplied load source.
16
+
17
+ Fix: after `idleUnloadMs` (default 300000 = 5 min) with no `embed()`, the pipeline
18
+ is disposed (thread pool freed) and lazily reloaded on the next embed. A rolling
19
+ timer resets on every embed (active agents stay resident), an `inFlight` guard
20
+ prevents disposing mid-batch, and the timer is `unref()`'d. Set `idleUnloadMs: 0`
21
+ to keep the prior keep-resident behavior. Verified live: idle CPU 3.6% → 0.0% after
22
+ dispose, and dispose→reload→embed produces an identical vector.
23
+
24
+ ## What to Tell Your User
25
+
26
+ Each agent keeps a small local AI model loaded to search its own memories, and the
27
+ engine running that model was quietly burning CPU even when the agent was idle —
28
+ its worker threads spin in a tight loop instead of sleeping. On a machine running
29
+ several agents, that added up to real wasted CPU. I changed it so the model unloads
30
+ after a few minutes of not being used and reloads in about a second the next time
31
+ it's needed. Active memory work is unaffected (the timer resets every time it's
32
+ used); only genuinely idle agents stop wasting CPU. I confirmed the model produces
33
+ the exact same results after an unload-and-reload, so nothing is lost.
34
+
35
+ ## Summary of New Capabilities
36
+
37
+ | Capability | How to Use |
38
+ |-----------|-----------|
39
+ | Idle-unload of the embedding model | Automatic — frees the ONNX thread pool after ~5 min idle, reloads on next use |
40
+ | Keep the model resident | Construct the embedding provider with the idle window set to zero |
41
+
42
+ ## Evidence
43
+
44
+ - **Live measurement:** loaded-but-idle model used ~3.6% of a core (3s window,
45
+ `process.cpuUsage()`); ~44% previously observed on a contended agent. After
46
+ dispose: 0.0%. dispose→reload→embed produced an identical embedding.
47
+ - **Tests:** `tests/unit/EmbeddingProvider-idle-unload.test.ts` (5, mock pipeline +
48
+ fake timers) — dispose-after-idle + lazy reload, resident-while-active,
49
+ disable-via-zero, inFlight guard, explicit dispose. `tsc` clean.
50
+ - Spec: `docs/specs/onnx-idle-unload.md`. Side-effects:
51
+ `upgrades/side-effects/onnx-idle-unload.md`.
@@ -0,0 +1,36 @@
1
+ # Side-effects — EmbeddingProvider idle-unload
2
+
3
+ **Change:** `EmbeddingProvider` disposes its loaded ONNX pipeline after
4
+ `idleUnloadMs` (default 5 min) of no `embed()` call, lazily reloading on the next
5
+ embed. New `inFlight` guard, rolling idle timer, and a public `dispose()`.
6
+
7
+ ## Behavioral side-effects
8
+
9
+ - **First embed after a long idle costs ~1-3s more** (a model reload). All
10
+ subsequent embeds during active use are unaffected — the idle timer resets on
11
+ every embed, so a busy agent keeps the model resident. Memory search/index are
12
+ not latency-critical, so the occasional reload is an acceptable trade for
13
+ eliminating the idle thread-pool spin (~3.6%-44% of a core).
14
+ - **`isReady` is now dynamic.** Previously it returned true forever once loaded;
15
+ now it returns false while the model is unloaded between idle periods. No caller
16
+ uses `isReady` to gate embeds (verified) — `embed()` lazily re-initializes
17
+ regardless — so this is observational only.
18
+ - **Embeddings are unchanged.** Verified live: dispose→reload→embed produces a
19
+ byte-identical vector. Unloading/reloading never alters output.
20
+ - **Default-ON.** The production singleton constructs with defaults, so all agents
21
+ get 5-min idle-unload on update. Set `idleUnloadMs: 0` (constructor config) to
22
+ restore the prior keep-resident behavior.
23
+
24
+ ## Blast radius
25
+
26
+ - Scoped to `src/memory/EmbeddingProvider.ts`. SemanticMemory / MemoryIndex /
27
+ TopicMemory consume it transparently (they call `embed()`/`embedBatch()`, which
28
+ handle reload internally). No call-site changes.
29
+ - The idle timer is `unref()`'d — it can never keep the process alive.
30
+ - No config schema, hook, or HTTP changes. No new dependencies.
31
+
32
+ ## Migration parity
33
+
34
+ None required — internal runtime logic in `src/`. No agent-installed file
35
+ (`.claude/settings.json`, `.instar/config.json`, CLAUDE.md template, hook, skill)
36
+ changes. Existing agents receive it on their normal version update.