@quantiya/codevibe-claude-plugin 2.0.71 → 2.0.73

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.
@@ -6349,7 +6349,9 @@ function createSourcePair(options) {
6349
6349
  discardUntilNewline: row3.discardUntilNewline
6350
6350
  }), earliest = (rows) => {
6351
6351
  let ordered = [...rows].sort((a, b) => a.byteOffset - b.byteOffset);
6352
- return ordered.length === 2 && ordered[0].byteOffset === ordered[1].byteOffset && JSON.stringify(position2(ordered[0])) !== JSON.stringify(position2(ordered[1])) ? options.invalid() : position2(ordered[0]);
6352
+ if (ordered.length === 2 && ordered[0].byteOffset === ordered[1].byteOffset && JSON.stringify(position2(ordered[0])) !== JSON.stringify(position2(ordered[1]))) return options.invalid();
6353
+ let baseline = position2(ordered[0]), pending = rows.flatMap((row3) => row3.entries.filter((entry5) => entry5.state === "PREPARING" && entry5.frame.startOffset === baseline.byteOffset).map((entry5) => entry5.frame.endOffset));
6354
+ return { ...baseline, ...pending.length && { pendingFrameEndOffset: Math.min(...pending) } };
6353
6355
  };
6354
6356
  return Object.freeze({
6355
6357
  open: (initial) => {
@@ -6377,6 +6379,7 @@ function createSourcePair(options) {
6377
6379
  skip: (gap2) => {
6378
6380
  let snapshot2 = { ...gap2, anchor: { ...gap2.anchor } };
6379
6381
  return options.locked(async () => {
6382
+ if (await recover(), (await Promise.all(lanes.map((lane) => lane.load()))).some((row3) => row3?.entries.some((entry5) => entry5.state === "PREPARING"))) return options.invalid();
6380
6383
  for (let lane of lanes) await lane.skip(snapshot2);
6381
6384
  });
6382
6385
  },
@@ -9052,7 +9055,7 @@ var count3 = import_zod9.z.string().regex(/^(0|[1-9][0-9]{0,18})$/).refine((s) =
9052
9055
  return ready.catch(() => {
9053
9056
  }), this.starting = Promise.resolve().then(async () => {
9054
9057
  try {
9055
- this.fence(), this.credential = await this.deps.reader.credential(this.abort.signal, !0), this.fence();
9058
+ this.fence(), this.credential = await this.deps.reader.credential(this.abort.signal, this.deps.revalidateRegistration !== !1), this.fence(), this.deps.revalidateRegistration !== !1 && this.deps.onRegistrationRevalidated?.();
9056
9059
  let auth = await this.deps.wire.socketProof("CONNECT", this.credential, void 0, this.abort.signal);
9057
9060
  this.fence();
9058
9061
  let open15 = this.deps.openSocket ?? ((url, options) => new import_ws.default(url, options)), ws = this.ws = open15(this.endpoint, {
@@ -9062,7 +9065,13 @@ var count3 = import_zod9.z.string().regex(/^(0|[1-9][0-9]{0,18})$/).refine((s) =
9062
9065
  followRedirects: !1,
9063
9066
  handshakeTimeout: DEADLINE
9064
9067
  });
9065
- ws.on("error", () => this.fail(failure())), ws.on("close", () => this.fail(failure())), ws.on("message", (data, binary) => this.message(data, binary)), ws.on("pong", (data) => {
9068
+ ws.on("error", () => this.fail(failure())), ws.on("unexpected-response", (_request, response) => {
9069
+ try {
9070
+ this.fence(), (response.statusCode === 401 || response.statusCode === 403) && this.deps.onUpgradeRejected?.();
9071
+ } catch {
9072
+ }
9073
+ response.resume(), this.fail(failure());
9074
+ }), ws.on("close", () => this.fail(failure())), ws.on("message", (data, binary) => this.message(data, binary)), ws.on("pong", (data) => {
9066
9075
  this.pingNonce && data.equals(this.pingNonce) && (clearTimeout(this.pongTimer), this.pongTimer = void 0, this.pingNonce = void 0);
9067
9076
  }), ws.on("open", () => {
9068
9077
  this.sendControl("hello").catch((error) => this.fail(error));
@@ -11852,6 +11861,9 @@ var AppSyncClient = class _AppSyncClient {
11852
11861
  endpoint: config.aws.syncWebSocketUrl,
11853
11862
  environment: this.environment,
11854
11863
  scope,
11864
+ revalidateRegistration: options.revalidateRegistration,
11865
+ onRegistrationRevalidated: options.onRegistrationRevalidated,
11866
+ onUpgradeRejected: options.onUpgradeRejected,
11855
11867
  wire: client,
11856
11868
  reader,
11857
11869
  inbound: writer.inbound(),
@@ -11922,7 +11934,8 @@ var AppSyncClient = class _AppSyncClient {
11922
11934
  omitNewPayload: async (signal) => {
11923
11935
  this.assertSyncAccount(userId), _AppSyncClient.throwIfAborted(signal);
11924
11936
  let observed = this.companionSyncStatusSnapshot(userId), state = observed?.mode === "SHADOW" ? observed : await this.getCompanionSyncState(signal);
11925
- if (this.assertSyncAccount(userId), _AppSyncClient.throwIfAborted(signal), state.protocol !== "SYNC_V2") throw new SyncEventClientError("SYNC_PRODUCER_FROZEN");
11937
+ if (this.assertSyncAccount(userId), _AppSyncClient.throwIfAborted(signal), state.protocol !== "SYNC_V2" && !(state.mode === "SHADOW" && state.protocol === "LEGACY"))
11938
+ throw new SyncEventClientError("SYNC_PRODUCER_FROZEN");
11926
11939
  return state.usage?.syncState === "PAUSED";
11927
11940
  }
11928
11941
  });
@@ -11984,8 +11997,15 @@ var AppSyncClient = class _AppSyncClient {
11984
11997
  let selected = await this.selectCompanionSourceTransport({ sessionId, root });
11985
11998
  if (check(), selected.protocol === "LEGACY") {
11986
11999
  let prior = await this.desktopSyncWriter({ root }).shellSources();
11987
- if (check(), prior.some((row3) => row3.sessionId === sessionId && row3.pendingCount > 0))
11988
- throw owner.recovery.start(), new SyncEventClientError("SYNC_PRODUCER_FROZEN");
12000
+ if (check(), prior.some((row3) => row3.sessionId === sessionId && row3.pendingCount > 0)) {
12001
+ try {
12002
+ await this.recoverPreparedCompanionEvents({ sessionId, root }, void 0, prior);
12003
+ } catch (error) {
12004
+ throw owner.recovery.start(), error;
12005
+ }
12006
+ if (check(), (await this.desktopSyncWriter({ root }).shellSources()).some((row3) => row3.sessionId === sessionId && row3.pendingCount > 0))
12007
+ throw owner.recovery.start(), new SyncEventClientError("SYNC_PRODUCER_FROZEN");
12008
+ }
11989
12009
  let result = await legacy();
11990
12010
  return check(), { protocol: "LEGACY", value: result };
11991
12011
  }
@@ -12021,7 +12041,7 @@ var AppSyncClient = class _AppSyncClient {
12021
12041
  this.assertSyncAccount(userId);
12022
12042
  let settled = resolved;
12023
12043
  for (let source2 of sources) {
12024
- if (source2.sessionId !== sessionId || source2.pendingCount === 0) continue;
12044
+ if (source2.sessionId !== sessionId || (this.rememberSyncRegistration({ sessionId, sessionGenerationId: source2.sessionGenerationId, root }), source2.pendingCount === 0)) continue;
12025
12045
  _AppSyncClient.throwIfAborted(signal), this.assertSyncAccount(userId);
12026
12046
  let cursor = this.desktopSyncWriter({ root, sessionScope: {
12027
12047
  sessionId,
@@ -12079,7 +12099,7 @@ var AppSyncClient = class _AppSyncClient {
12079
12099
  this.assertSyncAccount(userId), await recoverToolActivityWindows({ sessionId, outbox, ledger, transportForGeneration: (sessionGenerationId) => {
12080
12100
  this.assertSyncAccount(userId);
12081
12101
  let sessionScope = { sessionId, sessionGenerationId };
12082
- return createToolActivitySyncTransport({
12102
+ return this.rememberSyncRegistration({ ...sessionScope, root }), createToolActivitySyncTransport({
12083
12103
  sessionScope,
12084
12104
  writer: this.desktopSyncWriter({ root, sessionScope }),
12085
12105
  recoveryOnly: !0,
@@ -12114,14 +12134,29 @@ var AppSyncClient = class _AppSyncClient {
12114
12134
  this.assertSyncAccount(userId);
12115
12135
  let state = await this.getCompanionSyncRoute();
12116
12136
  if (this.assertSyncAccount(userId), state.protocol === "FROZEN") throw new SyncEventClientError("SYNC_PRODUCER_FROZEN");
12117
- if (state.protocol === "LEGACY") return Object.freeze({ protocol: "LEGACY" });
12137
+ let existingGeneration = options.existingSessionGenerationId;
12138
+ if (state.protocol === "LEGACY") {
12139
+ if (this.companionSyncStatusSnapshot(userId)?.mode !== "SHADOW") {
12140
+ if (existingGeneration === void 0) return Object.freeze({ protocol: "LEGACY" });
12141
+ throw new SyncEventClientError("SYNC_PRODUCER_FROZEN");
12142
+ }
12143
+ if (existingGeneration === void 0) {
12144
+ let generations = new Set([...this.syncRegistrations.get(sessionId)?.values() ?? []].filter((row3) => row3.userId === userId && (row3.scope.root ?? null) === (root ?? null)).map((row3) => row3.scope.sessionGenerationId));
12145
+ if (!generations.size) return Object.freeze({ protocol: "LEGACY" });
12146
+ if (generations.size !== 1) throw new SyncEventClientError("SYNC_SESSION_GENERATION_CHANGED");
12147
+ existingGeneration = [...generations][0];
12148
+ }
12149
+ }
12118
12150
  let readSession = async () => {
12119
12151
  this.assertSyncAccount(userId);
12120
12152
  let session3 = await this.getSession(sessionId);
12121
12153
  if (this.assertSyncAccount(userId), !session3 || session3.sessionId !== sessionId || session3.userId !== userId || session3.status !== "ACTIVE" || session3.isEncrypted !== !0 || typeof session3.sessionGenerationId != "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(session3.sessionGenerationId))
12122
12154
  throw new SyncEventClientError("SYNC_SESSION_UNAVAILABLE");
12123
12155
  return session3;
12124
- }, session2 = await readSession(), scope = Object.freeze({ sessionId, sessionGenerationId: session2.sessionGenerationId.toLowerCase() }), getEncryptionContext = async () => {
12156
+ }, session2 = await readSession(), scope = Object.freeze({ sessionId, sessionGenerationId: session2.sessionGenerationId.toLowerCase() });
12157
+ if (existingGeneration !== void 0 && existingGeneration !== scope.sessionGenerationId)
12158
+ throw new SyncEventClientError("SYNC_SESSION_GENERATION_CHANGED");
12159
+ let getEncryptionContext = async () => {
12125
12160
  let current = await readSession();
12126
12161
  if (current.sessionGenerationId.toLowerCase() !== scope.sessionGenerationId)
12127
12162
  throw new SyncEventClientError("SYNC_SESSION_GENERATION_CHANGED");
@@ -12177,18 +12212,33 @@ var AppSyncClient = class _AppSyncClient {
12177
12212
  return Promise.reject(new SyncEventClientError("INVALID_SYNC_INPUT"));
12178
12213
  let { root, reRaiseToken } = options, sessionId = input.sessionId, userId = this.getCurrentUserId(), check = () => {
12179
12214
  this.assertSyncAccount(userId), this.assertSyncReadAdmission(sessionId);
12180
- }, work = (async () => {
12215
+ }, { timestamp: _retryClock, isEncrypted: _plain, ...source2 } = input, sourceSha256 = (0, import_node_crypto17.createHash)("sha256").update((0, import_json_freeze13.canonicalize)(source2)).digest("hex"), work = (async () => {
12181
12216
  check();
12182
12217
  let selected = await this.selectCompanionSourceTransport({ sessionId, root });
12183
12218
  if (check(), selected.protocol === "LEGACY") {
12184
12219
  let previous = await this.desktopSyncWriter({ root }).promptPublications();
12185
- if (check(), previous.some((row3) => row3.sessionId === sessionId && row3.promptId === prompt.promptId))
12186
- throw new SyncEventClientError("SYNC_PRODUCER_FROZEN");
12187
- let accepted = await legacy();
12188
- return check(), accepted ? "RERAISED" : "RETRY";
12220
+ check();
12221
+ let existing = previous.filter((row3) => row3.sessionId === sessionId && row3.promptId === prompt.promptId);
12222
+ if (existing.length) {
12223
+ if (existing.length !== 1) throw new SyncEventClientError("SYNC_SESSION_GENERATION_CHANGED");
12224
+ let result2 = await this.desktopSyncWriter({ root, sessionScope: existing[0] }).promptPublication(prompt.promptId).publish(sourceSha256, async (operationId2) => {
12225
+ let retained = await this.selectCompanionSourceTransport({
12226
+ sessionId,
12227
+ root,
12228
+ existingSessionGenerationId: existing[0].sessionGenerationId
12229
+ });
12230
+ if (check(), retained.protocol !== "SYNC_V2") throw new SyncEventClientError("SYNC_PRODUCER_FROZEN");
12231
+ return this.rememberSyncRegistration({ ...retained.sessionScope, root }), retained.prepareEvent(input, operationId2);
12232
+ }, { reRaiseToken });
12233
+ return check(), result2.status === "OMITTED" ? "OMITTED" : "RERAISED";
12234
+ } else {
12235
+ let accepted = await legacy();
12236
+ return check(), accepted ? "RERAISED" : "RETRY";
12237
+ }
12189
12238
  }
12239
+ if (selected.protocol !== "SYNC_V2") throw new SyncEventClientError("SYNC_PRODUCER_FROZEN");
12190
12240
  this.rememberSyncRegistration({ ...selected.sessionScope, root });
12191
- let { timestamp: _retryClock, isEncrypted: _plain, ...source2 } = input, sourceSha256 = (0, import_node_crypto17.createHash)("sha256").update((0, import_json_freeze13.canonicalize)(source2)).digest("hex"), result = await selected.writer.promptPublication(prompt.promptId).publish(
12241
+ let result = await selected.writer.promptPublication(prompt.promptId).publish(
12192
12242
  sourceSha256,
12193
12243
  (operationId2) => selected.prepareEvent(input, operationId2),
12194
12244
  { reRaiseToken }
@@ -12260,11 +12310,7 @@ var AppSyncClient = class _AppSyncClient {
12260
12310
  check();
12261
12311
  let resolutions = await writer.promptResolutions();
12262
12312
  check();
12263
- let knownV2 = [...publications, ...resolutions].some((row3) => row3.sessionId === input.sessionId && row3.promptId === input.promptId), durable = (control, selected) => writer.runPromptResolution(control, async (saved, fence) => {
12264
- let state2 = selected ?? await this.getCompanionSyncRoute();
12265
- if (await fence(), state2.protocol !== "SYNC_V2") throw new SyncEventClientError("SYNC_PRODUCER_FROZEN");
12266
- return this.finishPromptResolution(saved, options, fence);
12267
- });
12313
+ let knownV2 = [...publications, ...resolutions].some((row3) => row3.sessionId === input.sessionId && row3.promptId === input.promptId), durable = (control) => writer.runPromptResolution(control, async (saved, fence) => (await fence(), this.finishPromptResolution(saved, options, fence)));
12268
12314
  if (knownV2) return durable(prepareV2());
12269
12315
  let state = await this.getCompanionSyncRoute();
12270
12316
  if (check(), state.protocol === "LEGACY")
@@ -12274,7 +12320,7 @@ var AppSyncClient = class _AppSyncClient {
12274
12320
  return check(), legacy();
12275
12321
  }, options);
12276
12322
  if (state.protocol !== "SYNC_V2") throw new SyncEventClientError("SYNC_PRODUCER_FROZEN");
12277
- return durable(prepareV2(), state);
12323
+ return durable(prepareV2());
12278
12324
  })();
12279
12325
  return this.trackSyncClosure(input.sessionId, work.then(() => {
12280
12326
  }, () => {
@@ -12297,8 +12343,6 @@ var AppSyncClient = class _AppSyncClient {
12297
12343
  check();
12298
12344
  let records = (await writer.promptResolutions()).filter((row3) => row3.sessionId === sessionId);
12299
12345
  if (check(), !records.length) return 0;
12300
- let state = await this.getCompanionSyncRoute(signal);
12301
- if (check(), state.protocol !== "SYNC_V2") throw new SyncEventClientError("SYNC_PRODUCER_FROZEN");
12302
12346
  let completed = 0, firstFailure;
12303
12347
  for (let input of records) {
12304
12348
  check();
@@ -14368,8 +14412,18 @@ var AppSyncClient = class _AppSyncClient {
14368
14412
  let session2 = await this.getSession(state.sessionId);
14369
14413
  if (!current()) return;
14370
14414
  if (this.assertSyncAccount(userId), !session2?.sessionGenerationId || session2.userId !== userId || !["ACTIVE" /* ACTIVE */, "PAUSED" /* PAUSED */].includes(session2.status)) throw new SyncEventClientError("SYNC_SESSION_UNAVAILABLE");
14371
- let connection = await this.desktopSyncLiveSession(
14372
- { sessionId: state.sessionId, sessionGenerationId: session2.sessionGenerationId },
14415
+ let generation = session2.sessionGenerationId, connection = await this.desktopSyncLiveSession(
14416
+ {
14417
+ sessionId: state.sessionId,
14418
+ sessionGenerationId: generation,
14419
+ revalidateRegistration: state.syncRegistrationRevalidationGeneration === generation,
14420
+ onRegistrationRevalidated: () => {
14421
+ current() && (state.syncRegistrationRevalidatedGeneration = generation, delete state.syncRegistrationRevalidationGeneration);
14422
+ },
14423
+ onUpgradeRejected: () => {
14424
+ current() && state.syncRegistrationRevalidatedGeneration !== generation && (state.syncRegistrationRevalidationGeneration = generation);
14425
+ }
14426
+ },
14373
14427
  (event) => {
14374
14428
  if (current() && shouldDeliverSubscribedEvent(event, !!state.receiveDesktopEvents)) return state.onEvent(event);
14375
14429
  },
@@ -14382,7 +14436,7 @@ var AppSyncClient = class _AppSyncClient {
14382
14436
  await connection.close();
14383
14437
  return;
14384
14438
  }
14385
- state.syncConnection = connection, state.syncSessionGenerationId = session2.sessionGenerationId, this.rememberSyncRegistration({ sessionId: state.sessionId, sessionGenerationId: session2.sessionGenerationId }), await connection.start(), current() && this.handleSubscriptionStartAck(state);
14439
+ state.syncConnection = connection, state.syncSessionGenerationId = session2.sessionGenerationId, this.rememberSyncRegistration({ sessionId: state.sessionId, sessionGenerationId: session2.sessionGenerationId }), await connection.start(), current() && (state.syncRegistrationRevalidationGeneration === generation && delete state.syncRegistrationRevalidationGeneration, state.syncRegistrationRevalidatedGeneration === generation && delete state.syncRegistrationRevalidatedGeneration, this.handleSubscriptionStartAck(state));
14386
14440
  })().catch((error) => {
14387
14441
  current() && this.handleSubscriptionError(state, error instanceof Error ? error : new Error("Sync connection failed"));
14388
14442
  });
@@ -14524,11 +14578,12 @@ var AppSyncClient = class _AppSyncClient {
14524
14578
  isUrgentPhase ? delay = Math.min(
14525
14579
  RECONNECT_CONFIG.baseDelayMs * Math.pow(RECONNECT_CONFIG.backoffMultiplier, state.reconnectAttempts - 1),
14526
14580
  RECONNECT_CONFIG.maxDelayMs
14527
- ) : (delay = RECONNECT_CONFIG.persistentDelayMs, state.reconnectAttempts === RECONNECT_CONFIG.urgentMaxAttempts + 1 && logger.info("[AppSyncClient] Switching to persistent reconnect (every 5min)", { sessionId })), logger.info("[AppSyncClient] Scheduling reconnect", {
14581
+ ) : (delay = RECONNECT_CONFIG.persistentDelayMs, state.reconnectAttempts === RECONNECT_CONFIG.urgentMaxAttempts + 1 && logger.info("[AppSyncClient] Switching to persistent reconnect (every 5min)", { sessionId })), error instanceof SyncReadClientError && Number.isSafeInteger(error.retryAfterMs) && error.retryAfterMs >= 0 && (delay = Math.max(delay, Math.min(error.retryAfterMs, 2147483647))), logger.info("[AppSyncClient] Scheduling reconnect", {
14528
14582
  sessionId,
14529
14583
  attempt: state.reconnectAttempts,
14530
14584
  phase: isUrgentPhase ? "urgent" : "persistent",
14531
- delayMs: delay
14585
+ delayMs: delay,
14586
+ ...error instanceof SyncReadClientError && { backendCode: error.backendCode, retryAfterMs: error.retryAfterMs }
14532
14587
  }), state.ws && (disposeWatcherSocket(state.ws), state.ws = null), state.keepAliveTimer && clearTimeout(state.keepAliveTimer), state.reconnectTimer = setTimeout(async () => {
14533
14588
  if (state.isReconnecting = !1, state.destroyed || this.activeSubscriptions.get(sessionId) !== state) {
14534
14589
  logger.info("[AppSyncClient] Reconnect skipped \u2014 state is no longer canonical", { sessionId });
@@ -117,6 +117,9 @@ export declare class ToolActivityConsolidator {
117
117
  private inFlight;
118
118
  /** Windows currently in backoff-limbo (neither queued nor in flight). */
119
119
  private retrying;
120
+ /** One session-wide pause for an explicit shared transport/status denial. */
121
+ private sendNotBefore;
122
+ private sendWakeTimer;
120
123
  private closed;
121
124
  /**
122
125
  * §4.5(b)/#232 — waiters woken by {@link deliver}'s finally so a bounded drain
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantiya/codevibe-core",
3
- "version": "2.0.66",
3
+ "version": "2.0.68",
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.71",
3
+ "version": "2.0.73",
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.66",
51
+ "@quantiya/codevibe-core": "^2.0.68",
52
52
  "@quantiya/quorum-core": "^1.0.1",
53
53
  "dotenv": "^16.6.1",
54
54
  "express": "^5.1.0",