@psiclawops/hypercompositor 0.8.2 → 0.8.5

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/dist/index.d.ts CHANGED
@@ -136,6 +136,32 @@ export declare function resolveEffectiveBudget(args: {
136
136
  budget: number;
137
137
  source: string;
138
138
  };
139
+ export interface ModelIdentity {
140
+ rawModel: string | null;
141
+ modelKey: string | null;
142
+ provider: string | null;
143
+ modelId: string | null;
144
+ }
145
+ export declare function resolveModelIdentity(model?: string): ModelIdentity;
146
+ export declare function diffModelState(previous: {
147
+ model?: string;
148
+ modelKey?: string | null;
149
+ provider?: string | null;
150
+ modelId?: string | null;
151
+ tokenBudget?: number;
152
+ } | null | undefined, current: {
153
+ model?: string;
154
+ tokenBudget?: number;
155
+ }): {
156
+ previousIdentity: ModelIdentity;
157
+ currentIdentity: ModelIdentity;
158
+ modelChanged: boolean;
159
+ providerChanged: boolean;
160
+ modelIdChanged: boolean;
161
+ budgetChanged: boolean;
162
+ budgetDownshift: boolean;
163
+ budgetUplift: boolean;
164
+ };
139
165
  /**
140
166
  * Bust the assembly cache for a specific agent+session.
141
167
  * Call this after writing to identity files (SOUL.md, IDENTITY.md, TOOLS.md,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -400,6 +400,48 @@ export function resolveEffectiveBudget(args) {
400
400
  source: 'fallback contextWindowSize',
401
401
  };
402
402
  }
403
+ export function resolveModelIdentity(model) {
404
+ const modelKey = normalizeModelKey(model);
405
+ if (!modelKey) {
406
+ return {
407
+ rawModel: model ?? null,
408
+ modelKey: null,
409
+ provider: null,
410
+ modelId: null,
411
+ };
412
+ }
413
+ const slash = modelKey.indexOf('/');
414
+ return {
415
+ rawModel: model ?? null,
416
+ modelKey,
417
+ provider: slash > 0 ? modelKey.slice(0, slash) : null,
418
+ modelId: slash > 0 && slash < modelKey.length - 1 ? modelKey.slice(slash + 1) : modelKey,
419
+ };
420
+ }
421
+ export function diffModelState(previous, current) {
422
+ const previousIdentity = previous?.modelKey || previous?.provider || previous?.modelId
423
+ ? {
424
+ rawModel: previous.model ?? null,
425
+ modelKey: previous.modelKey ?? normalizeModelKey(previous.model),
426
+ provider: previous.provider ?? resolveModelIdentity(previous.model).provider,
427
+ modelId: previous.modelId ?? resolveModelIdentity(previous.model).modelId,
428
+ }
429
+ : resolveModelIdentity(previous?.model);
430
+ const currentIdentity = resolveModelIdentity(current.model);
431
+ const previousBudget = previous?.tokenBudget;
432
+ const currentBudget = current.tokenBudget;
433
+ const budgetChanged = previousBudget != null && currentBudget != null && previousBudget !== currentBudget;
434
+ return {
435
+ previousIdentity,
436
+ currentIdentity,
437
+ modelChanged: previousIdentity.modelKey !== currentIdentity.modelKey,
438
+ providerChanged: previousIdentity.provider !== currentIdentity.provider,
439
+ modelIdChanged: previousIdentity.modelId !== currentIdentity.modelId,
440
+ budgetChanged,
441
+ budgetDownshift: previousBudget != null && currentBudget != null && currentBudget < previousBudget,
442
+ budgetUplift: previousBudget != null && currentBudget != null && currentBudget > previousBudget,
443
+ };
444
+ }
403
445
  function normalizeModelKey(model) {
404
446
  if (!model)
405
447
  return null;
@@ -723,6 +765,31 @@ function extractTextFromInboundContent(content) {
723
765
  .map(part => part.text ?? '')
724
766
  .join('\n');
725
767
  }
768
+ function resolveAssistantTokenCount(msg, runtimeContext) {
769
+ const usage = msg.usage;
770
+ if (usage && typeof usage === 'object') {
771
+ const candidates = [
772
+ usage.total,
773
+ usage.totalTokens,
774
+ usage.total_tokens,
775
+ usage.output,
776
+ usage.outputTokens,
777
+ usage.output_tokens,
778
+ usage.completionTokens,
779
+ usage.completion_tokens,
780
+ ];
781
+ for (const candidate of candidates) {
782
+ if (typeof candidate === 'number' && Number.isFinite(candidate) && candidate > 0) {
783
+ return Math.floor(candidate);
784
+ }
785
+ }
786
+ }
787
+ const runtimeTokenCount = runtimeContext?.currentTokenCount;
788
+ if (typeof runtimeTokenCount === 'number' && Number.isFinite(runtimeTokenCount) && runtimeTokenCount > 0) {
789
+ return Math.floor(runtimeTokenCount);
790
+ }
791
+ return undefined;
792
+ }
726
793
  function collectNeutralToolPairStats(messages) {
727
794
  const callIds = new Set();
728
795
  const resultIds = new Set();
@@ -1291,10 +1358,10 @@ function createHyperMemEngine() {
1291
1358
  // Non-fatal: missing files are silently skipped.
1292
1359
  let identityBlock;
1293
1360
  try {
1294
- // Council agents live at workspace-council/<agentId>/
1361
+ // Council agents live at workspace/<agentId>/
1295
1362
  // Other agents at workspace/<agentId>/ — try council path first
1296
1363
  const homedir = os.homedir();
1297
- const councilPath = path.join(homedir, '.openclaw', 'workspace-council', agentId);
1364
+ const councilPath = path.join(homedir, '.openclaw', 'workspace', agentId);
1298
1365
  const workspacePath = path.join(homedir, '.openclaw', 'workspace', agentId);
1299
1366
  let wsPath = councilPath;
1300
1367
  try {
@@ -1326,7 +1393,7 @@ function createHyperMemEngine() {
1326
1393
  let _wsPathForSeed;
1327
1394
  try {
1328
1395
  const homedir2 = os.homedir();
1329
- const councilPath2 = path.join(homedir2, '.openclaw', 'workspace-council', agentId);
1396
+ const councilPath2 = path.join(homedir2, '.openclaw', 'workspace', agentId);
1330
1397
  const workspacePath2 = path.join(homedir2, '.openclaw', 'workspace', agentId);
1331
1398
  try {
1332
1399
  await fs.access(councilPath2);
@@ -1361,7 +1428,7 @@ function createHyperMemEngine() {
1361
1428
  // Post-warm pressure check: if messages.db had accumulated history,
1362
1429
  // warm() may have loaded the session straight to 80%+. Pre-trim now
1363
1430
  // so the first turn has headroom instead of starting saturated.
1364
- // This is the "restart at 98%" failure mode reported by Helm 2026-04-05:
1431
+ // This is the "restart at 98%" failure mode reported by Eve 2026-04-05:
1365
1432
  // JSONL truncation + Redis flush isn't enough if messages.db is still full
1366
1433
  // and warm() reloads it. Trim here closes the loop.
1367
1434
  try {
@@ -2063,23 +2130,32 @@ function createHyperMemEngine() {
2063
2130
  console.warn('[hypermem-plugin] assemble: Redis trim failed (non-fatal):', trimErr.message);
2064
2131
  }
2065
2132
  // ── Budget downshift: proactive reshape pass ───────────────────────────────────────
2066
- // If this session previously composed at a higher token budget (e.g. gpt-5.4
2067
- // claude-sonnet model switch), the Redis window is still sized for the old
2068
- // budget. trimHistoryToTokenBudget above trims by count but skips tool
2069
- // gradient logic. A downshift >10% triggers a full reshape: apply tool
2070
- // gradient at the new budget + trim, then write back before compose runs.
2071
- // This prevents several turns of compaction churn after a model switch.
2072
- //
2073
- // Bug fix: previously read from getWindow() which is always null here
2074
- // (afterTurn invalidates it every turn). Also fixed: was doing setWindow()
2075
- // then invalidateWindow() which is a write-then-delete no-op. Now reads
2076
- // from history list and writes back via replaceHistory().
2133
+ // Detect provider/model identity changes as well as raw budget changes.
2134
+ // Provider routing matters operationally because the same model family can
2135
+ // land on a different effective context window, for example Copilot Sonnet
2136
+ // vs direct Anthropic Sonnet. Only budget downshifts trigger the demoted
2137
+ // reshape guard, but verbose logs now show provider/model swaps even when
2138
+ // the effective budget stays flat or increases.
2077
2139
  let lastState = null;
2078
2140
  try {
2079
2141
  lastState = await hm.cache.getModelState(agentId, sk);
2080
2142
  const DOWNSHIFT_THRESHOLD = 0.10;
2081
- const isDownshift = lastState &&
2082
- (lastState.tokenBudget - effectiveBudget) / lastState.tokenBudget > DOWNSHIFT_THRESHOLD;
2143
+ const modelDelta = diffModelState(lastState, {
2144
+ model,
2145
+ tokenBudget: effectiveBudget,
2146
+ });
2147
+ const downshiftFraction = lastState?.tokenBudget
2148
+ ? (lastState.tokenBudget - effectiveBudget) / lastState.tokenBudget
2149
+ : 0;
2150
+ const isDownshift = modelDelta.budgetDownshift && downshiftFraction > DOWNSHIFT_THRESHOLD;
2151
+ if (lastState && (modelDelta.modelChanged || modelDelta.budgetChanged)) {
2152
+ verboseLog(`[hypermem-plugin] model state change: ` +
2153
+ `prev=${modelDelta.previousIdentity.modelKey ?? 'unknown'} ` +
2154
+ `next=${modelDelta.currentIdentity.modelKey ?? 'unknown'} ` +
2155
+ `providerChanged=${modelDelta.providerChanged} ` +
2156
+ `modelIdChanged=${modelDelta.modelIdChanged} ` +
2157
+ `budget=${lastState.tokenBudget}->${effectiveBudget}`);
2158
+ }
2083
2159
  if (isDownshift && !_deferToolPruning) {
2084
2160
  // Sprint 2.2a: demote reshape to guard telemetry.
2085
2161
  //
@@ -2244,8 +2320,12 @@ ${replayRecovery.emittedText}`
2244
2320
  await persistReplayRecoveryState(hm, agentId, sk, replayRecovery.nextState);
2245
2321
  // Update model state for downshift detection on next turn
2246
2322
  try {
2323
+ const modelIdentity = resolveModelIdentity(model);
2247
2324
  await hm.cache.setModelState(agentId, sk, {
2248
2325
  model: model ?? 'unknown',
2326
+ modelKey: modelIdentity.modelKey ?? undefined,
2327
+ provider: modelIdentity.provider ?? undefined,
2328
+ modelId: modelIdentity.modelId ?? undefined,
2249
2329
  tokenBudget: effectiveBudget,
2250
2330
  composedAt: new Date().toISOString(),
2251
2331
  historyDepth,
@@ -2481,7 +2561,7 @@ ${replayRecovery.emittedText}`
2481
2561
  * it never calls ingest() or ingestBatch(). So we must ingest the new
2482
2562
  * messages here, using messages.slice(prePromptMessageCount).
2483
2563
  */
2484
- async afterTurn({ sessionId, sessionKey, messages, prePromptMessageCount, isHeartbeat }) {
2564
+ async afterTurn({ sessionId, sessionKey, messages, prePromptMessageCount, isHeartbeat, runtimeContext }) {
2485
2565
  if (isHeartbeat)
2486
2566
  return;
2487
2567
  try {
@@ -2523,7 +2603,9 @@ ${replayRecovery.emittedText}`
2523
2603
  await hm.recordUserMessage(agentId, sk, stripMessageMetadata(neutral.textContent ?? ''));
2524
2604
  }
2525
2605
  else {
2526
- await hm.recordAssistantMessage(agentId, sk, neutral);
2606
+ await hm.recordAssistantMessage(agentId, sk, neutral, {
2607
+ tokenCount: neutral.role === 'assistant' ? resolveAssistantTokenCount(m, runtimeContext) : undefined,
2608
+ });
2527
2609
  }
2528
2610
  }
2529
2611
  // P3.1: Topic detection on the inbound user message
@@ -2583,7 +2665,7 @@ ${replayRecovery.emittedText}`
2583
2665
  // gradient-compressed window to budget before writing to Redis. Without
2584
2666
  // this, afterTurn writes up to 250 messages regardless of budget, causing
2585
2667
  // trimHistoryToTokenBudget to fire and trim ~200 messages on every
2586
- // subsequent assemble() — the churn loop seen in Helm's logs.
2668
+ // subsequent assemble() — the churn loop seen in Eve's logs.
2587
2669
  if (hm.cache.isConnected) {
2588
2670
  try {
2589
2671
  const modelState = await hm.cache.getModelState(agentId, sk);
@@ -2607,7 +2689,7 @@ ${replayRecovery.emittedText}`
2607
2689
  // If a session just finished a turn at >80% pressure, the NEXT turn's
2608
2690
  // incoming tool results (parallel web searches, large exec output, etc.)
2609
2691
  // will hit a window with no headroom — the ingestion wave failure mode
2610
- // (reported by Helm, 2026-04-05). Pre-trim here so the tool-loop
2692
+ // (reported by Eve, 2026-04-05). Pre-trim here so the tool-loop
2611
2693
  // assemble() path starts the next turn with meaningful space.
2612
2694
  //
2613
2695
  // Uses modelState.tokenBudget if cached; skips if unavailable (non-fatal).
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@psiclawops/hypercompositor",
3
- "version": "0.8.2",
4
- "description": "HyperCompositor \u2014 context engine plugin for OpenClaw",
3
+ "version": "0.8.5",
4
+ "description": "HyperCompositor context engine plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "license": "Apache-2.0",
@@ -18,7 +18,7 @@
18
18
  "openclaw": {
19
19
  "plugin": {
20
20
  "id": "hypercompositor",
21
- "name": "HyperCompositor \u2014 Context Engine",
21
+ "name": "HyperCompositor Context Engine",
22
22
  "kind": "context-engine"
23
23
  },
24
24
  "extensions": [
@@ -38,12 +38,12 @@
38
38
  "typecheck": "tsc --noEmit"
39
39
  },
40
40
  "dependencies": {
41
- "@psiclawops/hypermem": "file:.."
41
+ "@psiclawops/hypermem": "file:..",
42
+ "zod": "^4.0.0"
42
43
  },
43
44
  "devDependencies": {
44
45
  "openclaw": "*",
45
- "typescript": "^5.4.0",
46
- "zod": "^4.0.0"
46
+ "typescript": "^5.4.0"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "openclaw": "*"