entroly-openclaw 1.0.59 → 1.0.61

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
@@ -68,10 +68,22 @@ local JSONL bridge before inviting users onto the Gateway. The plugin requires
68
68
  the Entroly 1.0.57 bridge v2 protocol; doctor reports an actionable upgrade
69
69
  instead of accepting an older, incompatible Python installation.
70
70
 
71
- OpenClaw's resolved prompt token budget is authoritative. Entroly never guesses
72
- a context window from the provider name. For a custom model whose limit
73
- OpenClaw cannot resolve, configure that model's context window in OpenClaw or
74
- set an explicit operator-approved fallback:
71
+ OpenClaw's resolved prompt token budget is authoritative. When an older or
72
+ degraded host cannot provide one, Entroly automatically resolves a conservative
73
+ input ceiling from its model registry. Only `verified`, operator-supplied
74
+ `user`, or explicitly `discovered` metadata is accepted; announced and unknown
75
+ records are rejected. The receipt records the model id, trust state, registry
76
+ digest, native window, output reserve, safety reserve, and source.
77
+
78
+ Context-budget discovery is local and enabled by default. It does not enable
79
+ remote provider discovery or send credentials anywhere. Local Ollama/LM Studio
80
+ and remote OpenRouter discovery remain separate operator opt-ins through
81
+ Entroly's model-registry environment controls. Disable this fallback with
82
+ `autoDiscoverContextBudget: false`.
83
+
84
+ For a custom model that neither OpenClaw nor trusted registry metadata can
85
+ resolve, configure the model in OpenClaw, provide an `ENTROLY_MODEL_REGISTRY`
86
+ override, or set an explicit last-resort fallback:
75
87
 
76
88
  ```json5
77
89
  {
@@ -83,8 +95,10 @@ set an explicit operator-approved fallback:
83
95
  }
84
96
  ```
85
97
 
86
- Without either budget, Entroly returns the exact original context with an
87
- actionable warning instead of risking a provider overflow.
98
+ Without any trusted budget, Entroly returns the exact original context with an
99
+ actionable warning instead of risking a provider overflow. Explicit OpenClaw
100
+ budgets always win, followed by the operator's `fallbackTokenBudget`; automatic
101
+ registry discovery is used only when neither is available.
88
102
 
89
103
  ## Reproduce the evidence-pinning control
90
104
 
package/engine.js CHANGED
@@ -42,6 +42,9 @@ function statusSnapshot(status) {
42
42
  "tokens_saved",
43
43
  "evidence_pinned",
44
44
  "evidence_pin_blocked",
45
+ "context_window",
46
+ "context_output_reserve",
47
+ "context_safety_tokens",
45
48
  ];
46
49
  const booleanFields = ["changed", "provider_independent"];
47
50
  const stringFields = [
@@ -49,6 +52,9 @@ function statusSnapshot(status) {
49
52
  "receipt_id",
50
53
  "provider_mode",
51
54
  "budget_source",
55
+ "context_discovery_status",
56
+ "context_discovery_trust",
57
+ "context_discovery_model",
52
58
  "model",
53
59
  "provider_hint",
54
60
  "assembly_strategy",
@@ -127,6 +133,7 @@ function resolveAssemblyRuntime({ tokenBudget, model, runtimeSettings, fallbackT
127
133
  prompt_token_budget: runtimeBudget ?? null,
128
134
  max_output_tokens: positiveInteger(runtimeSettings?.limits?.maxOutputTokens) ?? null,
129
135
  },
136
+ context_discovery: null,
130
137
  };
131
138
 
132
139
  if (explicitBudget !== undefined) {
@@ -161,6 +168,80 @@ function resolveAssemblyRuntime({ tokenBudget, model, runtimeSettings, fallbackT
161
168
  };
162
169
  }
163
170
 
171
+ function validateContextBudgetDiscovery(result) {
172
+ if (!result || result.ok !== true || result.schema_version !== ENTROLY_BRIDGE_SCHEMA) {
173
+ throw new Error("Entroly bridge returned an invalid context-discovery response");
174
+ }
175
+ if (result.status === "unavailable") {
176
+ return {
177
+ available: false,
178
+ metadata: {
179
+ status: "unavailable",
180
+ trust: boundedString(result.trust, 32),
181
+ model_id: boundedString(result.model, 256),
182
+ exact: null,
183
+ context_window: null,
184
+ output_reserve_tokens: null,
185
+ safety_tokens: null,
186
+ registry_digest: boundedString(result.registry_digest, 64),
187
+ source: null,
188
+ },
189
+ warning: safeDiagnostic(result.warning ?? "trusted model metadata was unavailable"),
190
+ };
191
+ }
192
+ if (result.status !== "resolved" || result.budget_source !== "entroly_model_registry") {
193
+ throw new Error("Entroly bridge returned an unknown context-discovery state");
194
+ }
195
+ const tokenBudget = positiveInteger(result.token_budget);
196
+ const contextWindow = positiveInteger(result.context_window);
197
+ const outputReserve = positiveInteger(result.output_reserve_tokens);
198
+ const safetyTokens = positiveInteger(result.safety_tokens);
199
+ const trust = boundedString(result.trust, 32);
200
+ const modelId = boundedString(result.model_id, 256);
201
+ const registryDigest = boundedString(result.registry_digest, 64);
202
+ if (
203
+ tokenBudget === undefined ||
204
+ tokenBudget < 1024 ||
205
+ contextWindow === undefined ||
206
+ outputReserve === undefined ||
207
+ safetyTokens === undefined ||
208
+ !["verified", "user", "discovered"].includes(trust) ||
209
+ modelId === null ||
210
+ registryDigest === null ||
211
+ !/^[0-9a-f]{64}$/.test(registryDigest) ||
212
+ tokenBudget + outputReserve + safetyTokens > contextWindow
213
+ ) {
214
+ throw new Error("Entroly bridge returned unsafe context-discovery limits");
215
+ }
216
+ return {
217
+ available: true,
218
+ tokenBudget,
219
+ metadata: {
220
+ status: "resolved",
221
+ trust,
222
+ model_id: modelId,
223
+ exact: result.exact === true,
224
+ context_window: contextWindow,
225
+ output_reserve_tokens: outputReserve,
226
+ safety_tokens: safetyTokens,
227
+ registry_digest: registryDigest,
228
+ source: boundedString(result.source, 512),
229
+ },
230
+ };
231
+ }
232
+
233
+ function applyDiscoveredBudget(runtime, discovery) {
234
+ return {
235
+ ...runtime,
236
+ tokenBudget: discovery.tokenBudget,
237
+ budgetSource: "entroly_model_registry",
238
+ runtimeMetadata: {
239
+ ...runtime.runtimeMetadata,
240
+ context_discovery: discovery.metadata,
241
+ },
242
+ };
243
+ }
244
+
164
245
  function canonicalJson(value) {
165
246
  if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
166
247
  if (value && typeof value === "object") {
@@ -295,6 +376,14 @@ export function formatEntrolyStatus(status) {
295
376
  `Estimated reduction: ${reduction}% (${saved.toLocaleString()} tokens)`,
296
377
  `Changed: ${status.changed ? "yes" : "no"}`,
297
378
  ];
379
+ if (status.context_discovery_status === "resolved") {
380
+ lines.splice(
381
+ 3,
382
+ 0,
383
+ `Context discovery: ${status.context_discovery_trust ?? "trusted"} metadata for ${status.context_discovery_model ?? "active model"}`,
384
+ `Discovered window: ${(status.context_window ?? 0).toLocaleString()} tokens (${(status.context_output_reserve ?? 0).toLocaleString()} output reserve + ${(status.context_safety_tokens ?? 0).toLocaleString()} safety reserve)`,
385
+ );
386
+ }
298
387
  if (status.receipt_id) lines.push(`Receipt: ${boundedString(status.receipt_id, 160)}`);
299
388
  if (status.warnings?.length) {
300
389
  lines.push(`Warnings: ${status.warnings.map((warning) => safeDiagnostic(warning)).join(" | ")}`);
@@ -390,12 +479,39 @@ export function createEntrolyContextEngine({
390
479
  throw error;
391
480
  }
392
481
  const sourceMessages = messages;
393
- const assemblyRuntime = resolveAssemblyRuntime({
482
+ let assemblyRuntime = resolveAssemblyRuntime({
394
483
  tokenBudget,
395
484
  model,
396
485
  runtimeSettings,
397
486
  fallbackTokenBudget: config.fallbackTokenBudget,
398
487
  });
488
+ if (
489
+ assemblyRuntime.tokenBudget === undefined &&
490
+ config.autoDiscoverContextBudget !== false &&
491
+ assemblyRuntime.model
492
+ ) {
493
+ try {
494
+ const discovery = validateContextBudgetDiscovery(
495
+ await bridge.request({
496
+ operation: "resolve_context_budget",
497
+ model: assemblyRuntime.model,
498
+ provider_hint: assemblyRuntime.runtimeMetadata.model.provider,
499
+ requested_output_tokens:
500
+ assemblyRuntime.runtimeMetadata.limits.max_output_tokens,
501
+ }),
502
+ );
503
+ if (discovery.available) {
504
+ assemblyRuntime = applyDiscoveredBudget(assemblyRuntime, discovery);
505
+ } else {
506
+ assemblyRuntime.runtimeMetadata.context_discovery = discovery.metadata;
507
+ logger.warn?.(`entroly: context auto-discovery unavailable: ${discovery.warning}`);
508
+ }
509
+ } catch (error) {
510
+ logger.warn?.(
511
+ `entroly: context auto-discovery failed safely: ${safeDiagnostic(error)}`,
512
+ );
513
+ }
514
+ }
399
515
  const preserveLastN = positiveInteger(config.preserveLastN) ?? 4;
400
516
  let systemPromptAddition;
401
517
  try {
@@ -419,6 +535,18 @@ export function createEntrolyContextEngine({
419
535
  provider_mode: PROVIDER_MODE,
420
536
  provider_independent: true,
421
537
  budget_source: assemblyRuntime.budgetSource,
538
+ context_discovery_status:
539
+ assemblyRuntime.runtimeMetadata.context_discovery?.status,
540
+ context_discovery_trust:
541
+ assemblyRuntime.runtimeMetadata.context_discovery?.trust,
542
+ context_discovery_model:
543
+ assemblyRuntime.runtimeMetadata.context_discovery?.model_id,
544
+ context_window:
545
+ assemblyRuntime.runtimeMetadata.context_discovery?.context_window,
546
+ context_output_reserve:
547
+ assemblyRuntime.runtimeMetadata.context_discovery?.output_reserve_tokens,
548
+ context_safety_tokens:
549
+ assemblyRuntime.runtimeMetadata.context_discovery?.safety_tokens,
422
550
  });
423
551
  return {
424
552
  messages: sourceMessages,
@@ -430,7 +558,7 @@ export function createEntrolyContextEngine({
430
558
  if (assemblyRuntime.tokenBudget === undefined) {
431
559
  return failOpen(
432
560
  new Error(
433
- "OpenClaw did not provide a positive prompt token budget; configure the model context window or plugins.entries.entroly.config.fallbackTokenBudget",
561
+ "OpenClaw did not provide a positive prompt token budget and Entroly could not resolve trusted model metadata; configure the model context window or plugins.entries.entroly.config.fallbackTokenBudget",
434
562
  ),
435
563
  );
436
564
  }
@@ -32,6 +32,10 @@
32
32
  "minimum": 1024,
33
33
  "maximum": 4000000
34
34
  },
35
+ "autoDiscoverContextBudget": {
36
+ "type": "boolean",
37
+ "default": true
38
+ },
35
39
  "receiptDir": {
36
40
  "type": "string",
37
41
  "minLength": 1
@@ -77,7 +81,11 @@
77
81
  },
78
82
  "fallbackTokenBudget": {
79
83
  "label": "Fallback Prompt Token Budget",
80
- "help": "Optional operator-approved budget used only when OpenClaw cannot resolve a finite model prompt budget."
84
+ "help": "Operator-approved budget used when OpenClaw cannot provide one. It takes precedence over automatic registry discovery."
85
+ },
86
+ "autoDiscoverContextBudget": {
87
+ "label": "Auto-discover Context Budget",
88
+ "help": "When OpenClaw omits a budget, resolve a conservative input ceiling from verified, user-supplied, or explicitly discovered model metadata. No remote discovery is enabled automatically."
81
89
  },
82
90
  "receiptDir": {
83
91
  "label": "Receipt Directory",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "entroly-openclaw",
3
- "version": "1.0.59",
3
+ "version": "1.0.61",
4
4
  "description": "Provider-independent, auditable context optimization for every OpenClaw model route",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",