@quantiya/codevibe-claude-plugin 2.0.45 → 2.0.46

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.
@@ -48439,8 +48439,15 @@ function projectDistilledForRole(entry, role, clearance, scope, retiredIds) {
48439
48439
  };
48440
48440
  return projected.task_specs.length === 0 && projected.outcomes.length === 0 && projected.artifact_refs.length === 0 && projected.surviving_decisions.length === 0 && projected.surviving_open_questions.length === 0 ? null : projected;
48441
48441
  }
48442
- var compactionIntegrityWarned = /* @__PURE__ */ new Set(), compactionsInFlight = /* @__PURE__ */ new Set();
48442
+ var compactionIntegrityWarned = /* @__PURE__ */ new Set(), compactionsInFlight = /* @__PURE__ */ new Set(), compactionChains = /* @__PURE__ */ new Map();
48443
48443
  async function compactSessionContext(deps) {
48444
+ let current = compactionChains.get(deps.sessionId) ?? Promise.resolve(), run2 = () => compactSessionContextLocked(deps), next = current.then(run2, run2), chain = next.catch(() => {
48445
+ });
48446
+ return compactionChains.set(deps.sessionId, chain), chain.finally(() => {
48447
+ compactionChains.get(deps.sessionId) === chain && compactionChains.delete(deps.sessionId);
48448
+ }), next;
48449
+ }
48450
+ async function compactSessionContextLocked(deps) {
48444
48451
  try {
48445
48452
  let verified = await readVerifiedContextItems(deps.sessionId);
48446
48453
  if (!verified.ok)
@@ -48564,7 +48571,6 @@ async function renderRehydratedSessionContext(deps) {
48564
48571
  for (let item of [...hot.slice(-RENDERED_HOT_ITEM_MAX)].reverse()) {
48565
48572
  let who = item.author.role === "agent" && item.author.agent_id ? item.author.agent_id : item.author.role, fact = bodyToFactText(item.body), rendered = (classifying || isBrainstorm) && item.kind === "turn" ? fact : capForRender(fact), line = `- [${item.kind}] ${who}: ${rendered}`;
48566
48573
  if (!take(hotLinesNewestFirst, line)) {
48567
- if (!classifying && !isBrainstorm) break;
48568
48574
  hotLinesNewestFirst.length === 0 && take(
48569
48575
  hotLinesNewestFirst,
48570
48576
  cutPreservingEnds(line, Math.min(sectionMax - used - 1, Math.floor(sectionMax * 2 / 3)))
@@ -48575,22 +48581,46 @@ async function renderRehydratedSessionContext(deps) {
48575
48581
  let hotLines = [...hotLinesNewestFirst].reverse();
48576
48582
  allocateMessages(USER_CONTEXT_MESSAGE_MAX_CHARS_PLANNER);
48577
48583
  let distilledLineGroups = [];
48578
- outer: for (let d of [...rehydrated.distilled].reverse()) {
48579
- let group = [];
48584
+ for (let d of [...rehydrated.distilled].reverse()) {
48585
+ let group = [], savedUsed = used, fits = !0;
48580
48586
  for (let spec of d.task_specs)
48581
- if (!take(group, `- Task: ${capForRender(bodyToFactText(spec.body))}`)) break outer;
48582
- for (let out of d.outcomes)
48583
- if (!take(group, ` Outcome: ${capForRender(bodyToFactText(out.body))}`)) break outer;
48584
- if (deps.purpose !== "classification") {
48587
+ if (!take(group, `- Task: ${capForRender(bodyToFactText(spec.body))}`)) {
48588
+ fits = !1;
48589
+ break;
48590
+ }
48591
+ if (fits) {
48592
+ for (let out of d.outcomes)
48593
+ if (!take(group, ` Outcome: ${capForRender(bodyToFactText(out.body))}`)) {
48594
+ fits = !1;
48595
+ break;
48596
+ }
48597
+ }
48598
+ if (fits && deps.purpose !== "classification") {
48585
48599
  for (let dec of d.surviving_decisions)
48586
- if (!isUserRuleBody(dec.body) && !take(group, ` Decision: ${capForRender(bodyToFactText(dec.body))}`))
48587
- break outer;
48588
- for (let q of d.surviving_open_questions)
48589
- if (!take(group, ` Open question: ${capForRender(bodyToFactText(q.body))}`)) break outer;
48590
- }
48591
- for (let a of d.artifact_refs)
48592
- if (!take(group, ` Artifact: ${capForRender(bodyToFactText(a.body))}`)) break outer;
48593
- distilledLineGroups.push(group);
48600
+ if (!isUserRuleBody(dec.body) && !take(group, ` Decision: ${capForRender(bodyToFactText(dec.body))}`)) {
48601
+ fits = !1;
48602
+ break;
48603
+ }
48604
+ if (fits) {
48605
+ for (let q of d.surviving_open_questions)
48606
+ if (!take(group, ` Open question: ${capForRender(bodyToFactText(q.body))}`)) {
48607
+ fits = !1;
48608
+ break;
48609
+ }
48610
+ }
48611
+ }
48612
+ if (fits) {
48613
+ for (let a of d.artifact_refs)
48614
+ if (!take(group, ` Artifact: ${capForRender(bodyToFactText(a.body))}`)) {
48615
+ fits = !1;
48616
+ break;
48617
+ }
48618
+ }
48619
+ if (!fits) {
48620
+ used = savedUsed;
48621
+ continue;
48622
+ }
48623
+ group.length > 0 && distilledLineGroups.push(group);
48594
48624
  }
48595
48625
  let distilledLines = distilledLineGroups.reverse().flat(), userLines = [];
48596
48626
  if (carriesUserList && userCandidates) {
@@ -127,11 +127,9 @@ export interface SessionCompactionCache {
127
127
  sessionId: string;
128
128
  /**
129
129
  * High-watermark `seq` — every item with `seq <= compactedThroughSeq` is
130
- * covered by `distilled`. `null` = nothing compacted yet. This is ALSO
131
- * the §6 startup/multi-device "local high-watermark seq ULID" (the LOCAL
132
- * half the hosted `seq > watermark` incremental pull rides with the
133
- * hosted upload path, which does not exist yet; flagged in the A1f
134
- * report).
130
+ * covered by `distilled`. `null` = nothing compacted yet. This is the LOCAL
131
+ * watermark; hosted blind-hold upload (shipped in A1e via `putContextItem`)
132
+ * tracks its own upload watermark in `hosted-sync.json`.
135
133
  */
136
134
  compactedThroughSeq: string | null;
137
135
  /**
@@ -238,6 +236,8 @@ export declare function projectDistilledForRole(entry: DistilledTaskSummary, rol
238
236
  *
239
237
  * LOSSLESS: the log is never touched — only the sidecar cache advances.
240
238
  * Fail-closed: an unverifiable chain compacts nothing. Never throws.
239
+ * Mutex-held per session: `onTaskBoundary` and `maybeFireCompactionSafetyValve`
240
+ * serialize strictly so a valve run never overwrites a boundary distillation.
241
241
  */
242
242
  export declare function compactSessionContext(deps: {
243
243
  sessionId: string;
@@ -402,3 +402,5 @@ export declare function gcContextItemLogs(deps: {
402
402
  export declare function onTaskBoundary(sessionId: string): Promise<void>;
403
403
  /** Test seam — reset module state (vitest-gated, the established pattern). */
404
404
  export declare function resetContextCompactionProcessStateForTests(): void;
405
+ /** Test seam — inspect compactionChains size to verify prompt cleanup upon settlement. */
406
+ export declare function getCompactionChainsSizeForTests(): number;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantiya/codevibe-core",
3
- "version": "2.0.40",
3
+ "version": "2.0.41",
4
4
  "description": "Core library for CodeVibe plugins - shared keychain, crypto, AppSync, and auth functionality",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantiya/codevibe-claude-plugin",
3
- "version": "2.0.45",
3
+ "version": "2.0.46",
4
4
  "description": "Control Claude Code from your iPhone and Android — real-time sync, approve file edits, send prompts by voice. Part of CodeVibe.",
5
5
  "main": "dist/server.js",
6
6
  "codevibe": {
@@ -48,7 +48,7 @@
48
48
  "node": ">=22.0.0"
49
49
  },
50
50
  "dependencies": {
51
- "@quantiya/codevibe-core": "^2.0.40",
51
+ "@quantiya/codevibe-core": "^2.0.41",
52
52
  "@quantiya/quorum-core": "^1.0.1",
53
53
  "dotenv": "^16.6.1",
54
54
  "express": "^5.1.0",