@indigoai-us/hq-cli 5.345.178 → 5.345.180

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.
Files changed (117) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/command-catalog.generated.d.ts +197 -1
  3. package/dist/command-catalog.generated.js +241 -1
  4. package/dist/commands/bot-jobs.d.ts +11 -9
  5. package/dist/commands/bot-jobs.js +56 -21
  6. package/dist/commands/bot.d.ts +37 -1
  7. package/dist/commands/bot.js +289 -13
  8. package/dist/commands/channels.d.ts +18 -1
  9. package/dist/commands/channels.js +185 -1
  10. package/dist/commands/daemon.d.ts +2 -0
  11. package/dist/commands/daemon.js +15 -4
  12. package/dist/commands/integrations-core.d.ts +2 -0
  13. package/dist/commands/integrations-core.js +1 -0
  14. package/dist/commands/lanes.d.ts +1 -1
  15. package/dist/commands/lanes.js +6 -2
  16. package/dist/commands/mesh.js +19 -0
  17. package/dist/commands/sources.d.ts +89 -0
  18. package/dist/commands/sources.js +363 -1
  19. package/dist/hq-cloud-sync-runner/bin/sync-runner.js +2285 -1743
  20. package/dist/hq-cloud-sync-runner/package.json +1 -1
  21. package/dist/lib/bot/config.d.ts +8 -0
  22. package/dist/lib/bot/fleet-local.d.ts +1 -1
  23. package/dist/lib/bot/fleet-local.js +82 -30
  24. package/dist/lib/bot/jobs/hot-signal-sync.d.ts +109 -0
  25. package/dist/lib/bot/jobs/hot-signal-sync.js +177 -0
  26. package/dist/lib/bot/local-jobs.d.ts +40 -4
  27. package/dist/lib/bot/local-jobs.js +72 -5
  28. package/dist/lib/bot/run.d.ts +15 -0
  29. package/dist/lib/bot/run.js +99 -1
  30. package/dist/lib/bot/runtime-auth.d.ts +119 -0
  31. package/dist/lib/bot/runtime-auth.js +251 -0
  32. package/dist/lib/daemon/catalog.d.ts +1 -0
  33. package/dist/lib/daemon/catalog.js +1 -1
  34. package/dist/lib/daemon/handoff.d.ts +4 -0
  35. package/dist/lib/daemon/handoff.js +6 -2
  36. package/dist/lib/daemon/install.d.ts +70 -2
  37. package/dist/lib/daemon/install.js +185 -27
  38. package/dist/lib/daemon/legacy.d.ts +9 -3
  39. package/dist/lib/daemon/legacy.js +11 -4
  40. package/dist/lib/daemon/migrate.js +1 -1
  41. package/dist/lib/daemon/run.d.ts +5 -2
  42. package/dist/lib/daemon/run.js +74 -22
  43. package/dist/lib/doctor/checks/anywhere.d.ts +6 -0
  44. package/dist/lib/doctor/checks/anywhere.js +35 -6
  45. package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
  46. package/dist/lib/flag-registry-worker-source.generated.js +1 -1
  47. package/dist/lib/hot-channels/adapters/__fixtures__/dummy.d.ts +33 -0
  48. package/dist/lib/hot-channels/adapters/__fixtures__/dummy.js +53 -0
  49. package/dist/lib/hot-channels/adapters/__fixtures__/meeting-providers.d.ts +38 -0
  50. package/dist/lib/hot-channels/adapters/__fixtures__/meeting-providers.js +64 -0
  51. package/dist/lib/hot-channels/adapters/attio.d.ts +67 -0
  52. package/dist/lib/hot-channels/adapters/attio.js +374 -0
  53. package/dist/lib/hot-channels/adapters/gmail.d.ts +111 -0
  54. package/dist/lib/hot-channels/adapters/gmail.js +413 -0
  55. package/dist/lib/hot-channels/adapters/index.d.ts +23 -0
  56. package/dist/lib/hot-channels/adapters/index.js +39 -0
  57. package/dist/lib/hot-channels/adapters/linear.d.ts +79 -0
  58. package/dist/lib/hot-channels/adapters/linear.js +321 -0
  59. package/dist/lib/hot-channels/adapters/meeting-providers.d.ts +86 -0
  60. package/dist/lib/hot-channels/adapters/meeting-providers.js +253 -0
  61. package/dist/lib/hot-channels/adapters/meetings.d.ts +81 -0
  62. package/dist/lib/hot-channels/adapters/meetings.js +525 -0
  63. package/dist/lib/hot-channels/adapters/slack.d.ts +70 -0
  64. package/dist/lib/hot-channels/adapters/slack.js +277 -0
  65. package/dist/lib/hot-channels/alert.d.ts +78 -0
  66. package/dist/lib/hot-channels/alert.js +179 -0
  67. package/dist/lib/hot-channels/audience.d.ts +15 -0
  68. package/dist/lib/hot-channels/audience.js +25 -0
  69. package/dist/lib/hot-channels/contract.d.ts +146 -0
  70. package/dist/lib/hot-channels/contract.js +20 -0
  71. package/dist/lib/hot-channels/cursors.d.ts +41 -0
  72. package/dist/lib/hot-channels/cursors.js +84 -0
  73. package/dist/lib/hot-channels/index.d.ts +10 -0
  74. package/dist/lib/hot-channels/index.js +10 -0
  75. package/dist/lib/hot-channels/landing.d.ts +83 -0
  76. package/dist/lib/hot-channels/landing.js +175 -0
  77. package/dist/lib/hot-channels/ledger.d.ts +101 -0
  78. package/dist/lib/hot-channels/ledger.js +203 -0
  79. package/dist/lib/hot-channels/loop.d.ts +74 -0
  80. package/dist/lib/hot-channels/loop.js +221 -0
  81. package/dist/lib/hot-channels/prompts/signals-pass.md +22 -0
  82. package/dist/lib/hot-channels/registry.d.ts +28 -0
  83. package/dist/lib/hot-channels/registry.js +121 -0
  84. package/dist/lib/hot-channels/run.d.ts +137 -0
  85. package/dist/lib/hot-channels/run.js +324 -0
  86. package/dist/lib/hot-channels/signals-pass.d.ts +150 -0
  87. package/dist/lib/hot-channels/signals-pass.js +581 -0
  88. package/dist/lib/hot-channels/sync-lock.d.ts +51 -0
  89. package/dist/lib/hot-channels/sync-lock.js +224 -0
  90. package/dist/lib/install/hook-target.d.ts +6 -2
  91. package/dist/lib/install/hook-target.js +9 -4
  92. package/dist/lib/lanes/accounts.d.ts +1 -17
  93. package/dist/lib/lanes/accounts.js +5 -45
  94. package/dist/lib/lanes/loop.d.ts +10 -0
  95. package/dist/lib/lanes/loop.js +18 -1
  96. package/dist/lib/lanes/provider-home.d.ts +24 -0
  97. package/dist/lib/lanes/provider-home.js +52 -0
  98. package/dist/lib/lanes/spawn.d.ts +12 -0
  99. package/dist/lib/lanes/spawn.js +27 -0
  100. package/dist/lib/mesh/live/daemon/doctor.d.ts +3 -0
  101. package/dist/lib/mesh/live/daemon/doctor.js +13 -0
  102. package/dist/lib/mesh/live/daemon/owner-inbox-push.d.ts +94 -0
  103. package/dist/lib/mesh/live/daemon/owner-inbox-push.js +273 -0
  104. package/dist/lib/mesh/live/daemon/run.js +31 -0
  105. package/dist/lib/mesh/live/daemon/work-subscribe.d.ts +14 -0
  106. package/dist/lib/mesh/live/daemon/work-subscribe.js +210 -3
  107. package/dist/lib/mesh/live/paths.d.ts +2 -0
  108. package/dist/lib/mesh/live/paths.js +2 -0
  109. package/dist/lib/sync-core/flag.d.ts +1 -1
  110. package/dist/lib/sync-core/flag.js +1 -1
  111. package/dist/lib/sync-core/reconcile.d.ts +1 -1
  112. package/dist/lib/sync-core/reconcile.js +9 -7
  113. package/dist/lib/sync-core/release.d.ts +3 -3
  114. package/dist/lib/sync-core/release.js +4 -4
  115. package/dist/lib/sync-core/rollout-state.d.ts +1 -0
  116. package/dist/lib/sync-core/rollout-state.js +1 -0
  117. package/package.json +8 -8
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Contract-2 subscriber for the personal work topic.
3
3
  * Owns live-cache.json writes. Presence publish stays on PresenceClient.
4
+ * On a person (prs_) account it also subscribes to the owner DM topic and
5
+ * keeps the content-free owner-inbox-push.json counter.
4
6
  */
5
7
  import * as fs from "node:fs";
6
8
  import * as path from "node:path";
@@ -10,6 +12,7 @@ import { presignIotWssUrl } from "./presign.js";
10
12
  import { applyProjectViewPush, parseProjectViewPush } from "./live-cache.js";
11
13
  import { backoffDelayMs, RECONNECT_MIN_DELAY_MS } from "./presence.js";
12
14
  import { AGENT_INBOX_PUSH_STATE_BASENAME, SYNC_WAKE_STATE_BASENAME } from "../paths.js";
15
+ import { clearOwnerInboxPushState, parseOwnerDoorbell, readOwnerInboxPushState, writeOwnerInboxPushState, } from "./owner-inbox-push.js";
13
16
  export { AGENT_INBOX_PUSH_STATE_BASENAME, SYNC_WAKE_STATE_BASENAME } from "../paths.js";
14
17
  const INBOX_PUSH_HEARTBEAT_MS = 5_000;
15
18
  const SYNC_WAKE_KEYS = new Set(["v", "type", "vault", "head", "rootId"]);
@@ -57,6 +60,13 @@ function readVaultSequences(raw) {
57
60
  }
58
61
  return vaults;
59
62
  }
63
+ const OWNER_SEEN_EVENT_LIMIT = 500;
64
+ /**
65
+ * Owner DM subscribe retries per connection. The owner signal is best-effort:
66
+ * after this many failures it stays off until the next broker reconnect, and
67
+ * the work topic keeps running.
68
+ */
69
+ export const OWNER_SUBSCRIBE_MAX_RETRIES = 6;
60
70
  function readWakeSequence(meshRoot, basename, label, onInfo) {
61
71
  const target = path.join(meshRoot, basename);
62
72
  try {
@@ -163,6 +173,39 @@ export async function startWorkCacheSubscriber(opts) {
163
173
  let syncWakeSequence = syncWakeState.sequence;
164
174
  const syncWakeVaults = { ...syncWakeState.vaults };
165
175
  const syncStateWritable = syncWakeState.writable;
176
+ let ownerSubscribed = false;
177
+ let ownerUid;
178
+ // Carry the owner counter forward across restarts. The file holds counts
179
+ // and timestamps only.
180
+ const priorOwnerState = readOwnerInboxPushState(opts.meshRoot, opts.onInfo);
181
+ let ownerKnown = priorOwnerState !== null;
182
+ let ownerState = {
183
+ connected: false,
184
+ heartbeatAtMs: priorOwnerState?.heartbeatAtMs ?? 0,
185
+ count: priorOwnerState?.count ?? 0,
186
+ ...(priorOwnerState?.lastAt ? { lastAt: priorOwnerState.lastAt } : {}),
187
+ ...(priorOwnerState?.lastType ? { lastType: priorOwnerState.lastType } : {}),
188
+ };
189
+ const seenOwnerEventIds = new Set();
190
+ let ownerRetryTimer = null;
191
+ let ownerRetryAttempt = 0;
192
+ // Each distinct owner subscribe failure is logged once per daemon lifetime.
193
+ const ownerFailuresLogged = new Set();
194
+ let ownerGiveUpLogged = false;
195
+ const clearOwnerRetry = () => {
196
+ if (ownerRetryTimer !== null)
197
+ timers.clearTimeout(ownerRetryTimer);
198
+ ownerRetryTimer = null;
199
+ };
200
+ const reportOwnerSubscribeFailure = (err) => {
201
+ // Log the error name only; the message can carry the topic and its uid.
202
+ const key = `${err.name}:${err.message}`;
203
+ if (ownerFailuresLogged.has(key))
204
+ return;
205
+ ownerFailuresLogged.add(key);
206
+ opts.onInfo?.(`owner inbox push subscribe failed (${err.name}); retrying with backoff, ` +
207
+ `up to ${OWNER_SUBSCRIBE_MAX_RETRIES} times per connection`);
208
+ };
166
209
  const clearInboxHeartbeat = () => {
167
210
  if (heartbeatTimer !== null)
168
211
  timers.clearTimeout(heartbeatTimer);
@@ -199,30 +242,79 @@ export async function startWorkCacheSubscriber(opts) {
199
242
  const persistSyncState = (connected, durable = true) => syncStateWritable
200
243
  ? persistState(SYNC_WAKE_STATE_BASENAME, "sync wake", connected, syncWakeSequence, undefined, durable, syncWakeVaults)
201
244
  : undefined;
245
+ const persistOwnerState = (connected) => {
246
+ if (!ownerKnown)
247
+ return;
248
+ ownerState = {
249
+ ...ownerState,
250
+ connected,
251
+ heartbeatAtMs: now().getTime(),
252
+ };
253
+ if (connected && ownerUid)
254
+ ownerState.principalUid = ownerUid;
255
+ else
256
+ delete ownerState.principalUid;
257
+ try {
258
+ writeOwnerInboxPushState(opts.meshRoot, ownerState);
259
+ }
260
+ catch (error) {
261
+ const name = error instanceof Error ? error.name : "UnknownError";
262
+ opts.onInfo?.(`owner inbox push state write failed (${name})`);
263
+ }
264
+ };
202
265
  const scheduleInboxHeartbeat = () => {
203
- if (stopped || (!inboxSubscribed && !syncSubscribed))
266
+ if (stopped || (!inboxSubscribed && !syncSubscribed && !ownerSubscribed))
204
267
  return;
205
268
  clearInboxHeartbeat();
206
269
  heartbeatTimer = timers.setTimeout(() => {
207
270
  heartbeatTimer = null;
208
- if (!stopped && (inboxSubscribed || syncSubscribed)) {
271
+ if (!stopped && (inboxSubscribed || syncSubscribed || ownerSubscribed)) {
209
272
  if (inboxSubscribed)
210
273
  persistInboxState(true, false);
211
274
  if (syncSubscribed)
212
275
  persistSyncState(true, false);
276
+ if (ownerSubscribed)
277
+ persistOwnerState(true);
213
278
  scheduleInboxHeartbeat();
214
279
  }
215
280
  }, INBOX_PUSH_HEARTBEAT_MS);
216
281
  };
282
+ const bumpOwnerCount = (type) => {
283
+ ownerState = {
284
+ ...ownerState,
285
+ count: ownerState.count + 1,
286
+ lastAt: now().toISOString(),
287
+ lastType: type,
288
+ };
289
+ persistOwnerState(true);
290
+ };
291
+ const rememberOwnerEvent = (eventId) => {
292
+ if (!eventId)
293
+ return true;
294
+ if (seenOwnerEventIds.has(eventId))
295
+ return false;
296
+ seenOwnerEventIds.add(eventId);
297
+ if (seenOwnerEventIds.size > OWNER_SEEN_EVENT_LIMIT) {
298
+ const oldest = seenOwnerEventIds.values().next().value;
299
+ if (oldest !== undefined)
300
+ seenOwnerEventIds.delete(oldest);
301
+ }
302
+ return true;
303
+ };
217
304
  const connect = async () => {
218
305
  if (stopped)
219
306
  return;
220
307
  inboxSubscribed = false;
221
308
  syncSubscribed = false;
222
309
  actorUid = undefined;
310
+ ownerSubscribed = false;
311
+ ownerUid = undefined;
312
+ ownerRetryAttempt = 0;
313
+ clearOwnerRetry();
223
314
  clearInboxHeartbeat();
224
315
  persistInboxState(false);
225
316
  persistSyncState(false);
317
+ persistOwnerState(false);
226
318
  const gen = ++generation;
227
319
  let bundle;
228
320
  try {
@@ -241,7 +333,25 @@ export async function startWorkCacheSubscriber(opts) {
241
333
  : undefined;
242
334
  const syncTopic = syncStateWritable ? `hq/${bundle.actorUid}/sync` : undefined;
243
335
  actorUid = inboxTopic || syncTopic ? bundle.actorUid : undefined;
244
- if (!topic && !inboxTopic && !syncTopic) {
336
+ const ownerTopic = bundle.actorUid.startsWith("prs_") &&
337
+ bundle.topics.dm === `hq/${bundle.actorUid}/dm`
338
+ ? bundle.topics.dm
339
+ : undefined;
340
+ const ownerActorUid = ownerTopic ? bundle.actorUid : undefined;
341
+ ownerUid = ownerActorUid;
342
+ if (!ownerTopic && ownerKnown) {
343
+ // Not a person account (or no owner DM topic): drop a file left by an
344
+ // earlier person login so push-status reports off rather than stale.
345
+ try {
346
+ clearOwnerInboxPushState(opts.meshRoot);
347
+ ownerKnown = false;
348
+ }
349
+ catch (error) {
350
+ const name = error instanceof Error ? error.name : "UnknownError";
351
+ opts.onInfo?.(`owner inbox push state clear failed (${name})`);
352
+ }
353
+ }
354
+ if (!topic && !inboxTopic && !syncTopic && !ownerTopic) {
245
355
  opts.onInfo?.("work cache subscribe skipped: credentials have no supported topic");
246
356
  return;
247
357
  }
@@ -257,6 +367,8 @@ export async function startWorkCacheSubscriber(opts) {
257
367
  if (stopped || gen !== generation)
258
368
  return;
259
369
  reconnectLogged = false;
370
+ // The owner DM topic is best-effort and is not counted here: its failure
371
+ // retries on its own and never ends the connection.
260
372
  let pendingSubscriptions = Number(Boolean(topic)) + Number(Boolean(inboxTopic)) +
261
373
  Number(Boolean(syncTopic));
262
374
  let subscriptionFailed = false;
@@ -282,6 +394,46 @@ export async function startWorkCacheSubscriber(opts) {
282
394
  if (pendingSubscriptions === 0)
283
395
  attempt = 0;
284
396
  };
397
+ if (pendingSubscriptions === 0)
398
+ attempt = 0;
399
+ const subscribeOwner = () => {
400
+ if (!ownerTopic)
401
+ return;
402
+ next.subscribe(ownerTopic, { qos: 1 }, (err) => {
403
+ if (stopped || gen !== generation)
404
+ return;
405
+ if (err) {
406
+ ownerSubscribed = false;
407
+ ownerUid = undefined;
408
+ persistOwnerState(false);
409
+ reportOwnerSubscribeFailure(err);
410
+ if (ownerRetryAttempt >= OWNER_SUBSCRIBE_MAX_RETRIES) {
411
+ if (!ownerGiveUpLogged) {
412
+ ownerGiveUpLogged = true;
413
+ opts.onInfo?.(`owner inbox push subscribe gave up after ${OWNER_SUBSCRIBE_MAX_RETRIES} retries; ` +
414
+ "owner push stays off until the next reconnect");
415
+ }
416
+ return;
417
+ }
418
+ const delay = backoffDelayMs(ownerRetryAttempt, 1000, 60_000, random, 0, RECONNECT_MIN_DELAY_MS);
419
+ ownerRetryAttempt += 1;
420
+ clearOwnerRetry();
421
+ ownerRetryTimer = timers.setTimeout(() => {
422
+ ownerRetryTimer = null;
423
+ if (stopped || gen !== generation)
424
+ return;
425
+ subscribeOwner();
426
+ }, delay);
427
+ return;
428
+ }
429
+ ownerRetryAttempt = 0;
430
+ ownerSubscribed = true;
431
+ ownerKnown = true;
432
+ ownerUid = ownerActorUid;
433
+ persistOwnerState(true);
434
+ scheduleInboxHeartbeat();
435
+ });
436
+ };
285
437
  if (topic) {
286
438
  next.subscribe(topic, { qos: 1 }, (err) => finishSubscription(err));
287
439
  }
@@ -320,6 +472,7 @@ export async function startWorkCacheSubscriber(opts) {
320
472
  finishSubscription();
321
473
  });
322
474
  }
475
+ subscribeOwner();
323
476
  });
324
477
  next.on("message", (_topic, payload) => {
325
478
  if (stopped || gen !== generation)
@@ -344,6 +497,48 @@ export async function startWorkCacheSubscriber(opts) {
344
497
  persistSyncState(true);
345
498
  return;
346
499
  }
500
+ if (ownerTopic && _topic === ownerTopic) {
501
+ if (!ownerSubscribed)
502
+ return;
503
+ // Read only type, channelId and eventId. Message text, poster name
504
+ // and email are never stored, logged or forwarded.
505
+ const doorbell = parseOwnerDoorbell(payload);
506
+ if (!doorbell)
507
+ return;
508
+ if (doorbell.channelId) {
509
+ const scope = opts.ownerChannelScope;
510
+ if (!scope)
511
+ return;
512
+ if (doorbell.eventId && seenOwnerEventIds.has(doorbell.eventId))
513
+ return;
514
+ const channelId = doorbell.channelId;
515
+ void scope
516
+ .isInScope(channelId)
517
+ .catch((error) => {
518
+ const name = error instanceof Error ? error.name : "UnknownError";
519
+ opts.onInfo?.(`owner channel scope check failed (${name})`);
520
+ return false;
521
+ })
522
+ .then((inScope) => {
523
+ if (!inScope || stopped || gen !== generation || !ownerSubscribed)
524
+ return;
525
+ // Record the event only once it counts, so a redelivery after a
526
+ // failed or out-of-scope check is evaluated again.
527
+ if (!rememberOwnerEvent(doorbell.eventId))
528
+ return;
529
+ bumpOwnerCount(doorbell.type);
530
+ });
531
+ return;
532
+ }
533
+ // A channel event must name its channel; without one its company is
534
+ // unknown, so it is dropped.
535
+ if (doorbell.type === "channel")
536
+ return;
537
+ if (!rememberOwnerEvent(doorbell.eventId))
538
+ return;
539
+ bumpOwnerCount(doorbell.type);
540
+ return;
541
+ }
347
542
  if (_topic !== topic)
348
543
  return;
349
544
  const push = parseProjectViewPush(payload);
@@ -360,9 +555,13 @@ export async function startWorkCacheSubscriber(opts) {
360
555
  inboxSubscribed = false;
361
556
  syncSubscribed = false;
362
557
  actorUid = undefined;
558
+ ownerSubscribed = false;
559
+ ownerUid = undefined;
560
+ clearOwnerRetry();
363
561
  clearInboxHeartbeat();
364
562
  persistInboxState(false);
365
563
  persistSyncState(false);
564
+ persistOwnerState(false);
366
565
  opts.onInfo?.("work cache mqtt error");
367
566
  schedule();
368
567
  try {
@@ -378,9 +577,13 @@ export async function startWorkCacheSubscriber(opts) {
378
577
  inboxSubscribed = false;
379
578
  syncSubscribed = false;
380
579
  actorUid = undefined;
580
+ ownerSubscribed = false;
581
+ ownerUid = undefined;
582
+ clearOwnerRetry();
381
583
  clearInboxHeartbeat();
382
584
  persistInboxState(false);
383
585
  persistSyncState(false);
586
+ persistOwnerState(false);
384
587
  schedule();
385
588
  });
386
589
  };
@@ -408,12 +611,16 @@ export async function startWorkCacheSubscriber(opts) {
408
611
  generation += 1;
409
612
  if (timer !== null)
410
613
  timers.clearTimeout(timer);
614
+ clearOwnerRetry();
411
615
  clearInboxHeartbeat();
412
616
  inboxSubscribed = false;
413
617
  syncSubscribed = false;
414
618
  actorUid = undefined;
619
+ ownerSubscribed = false;
620
+ ownerUid = undefined;
415
621
  persistInboxState(false);
416
622
  persistSyncState(false);
623
+ persistOwnerState(false);
417
624
  const current = client;
418
625
  client = null;
419
626
  if (!current)
@@ -5,6 +5,8 @@
5
5
  /** Content-free sequence file written by the mesh MQTT inbox subscriber. */
6
6
  export declare const AGENT_INBOX_PUSH_STATE_BASENAME = "agent-inbox-push.json";
7
7
  export declare const SYNC_WAKE_STATE_BASENAME = "sync-wake.json";
8
+ /** Content-free counter file written by the mesh MQTT owner DM subscriber. */
9
+ export declare const OWNER_INBOX_PUSH_STATE_BASENAME = "owner-inbox-push.json";
8
10
  export declare function workMeshRoot(home?: string, env?: NodeJS.ProcessEnv): string;
9
11
  export declare function workMeshSpoolPath(root?: string): string;
10
12
  export declare function workMeshHeldPath(root?: string): string;
@@ -8,6 +8,8 @@ const WORK_MESH_DIRNAME = "work-mesh";
8
8
  /** Content-free sequence file written by the mesh MQTT inbox subscriber. */
9
9
  export const AGENT_INBOX_PUSH_STATE_BASENAME = "agent-inbox-push.json";
10
10
  export const SYNC_WAKE_STATE_BASENAME = "sync-wake.json";
11
+ /** Content-free counter file written by the mesh MQTT owner DM subscriber. */
12
+ export const OWNER_INBOX_PUSH_STATE_BASENAME = "owner-inbox-push.json";
11
13
  export function workMeshRoot(home = os.homedir(), env = process.env) {
12
14
  const override = env.HQ_WORK_MESH_ROOT?.trim();
13
15
  if (override)
@@ -1,5 +1,5 @@
1
1
  import { type FlagClientOptions, type FlagResolution } from "@indigoai-us/hq-flags-client";
2
- export declare const SYNC_RUST_ENGINE_ROLLOUT_FLAG = "sync.rust-engine-rollout";
2
+ export declare const SYNC_RUST_ENGINE_ROLLOUT_FLAG = "sync.rust-engine-rollout-v2";
3
3
  export interface RolloutFlagClient {
4
4
  ready(): Promise<void>;
5
5
  explain(flagKey: string, options?: {
@@ -1,6 +1,6 @@
1
1
  import { createFlagClient, readLocalOverride } from "@indigoai-us/hq-flags-client";
2
2
  import { resolveFlagRegistryEndpoint } from "../flag-registry-endpoint.js";
3
- export const SYNC_RUST_ENGINE_ROLLOUT_FLAG = "sync.rust-engine-rollout";
3
+ export const SYNC_RUST_ENGINE_ROLLOUT_FLAG = "sync.rust-engine-rollout-v2";
4
4
  const READ_TIMEOUT_MS = 5_000;
5
5
  export class RolloutFlagReadError extends Error {
6
6
  }
@@ -26,6 +26,6 @@ export interface SyncCoreReconcileResult {
26
26
  kept: string[];
27
27
  warnings: string[];
28
28
  }
29
- export declare function companyFullHold(vault: SyncCoreVault, release: string): string | undefined;
29
+ export declare function fullModeHold(vault: SyncCoreVault, release: string): string | undefined;
30
30
  export declare function reconcileSyncCore(deps: SyncCoreReconcileDeps): Promise<SyncCoreReconcileResult>;
31
31
  //# sourceMappingURL=reconcile.d.ts.map
@@ -3,17 +3,18 @@ import * as path from "node:path";
3
3
  import { readCompaniesManifestMap } from "../work-context/repo-remote.js";
4
4
  import { readVaultSyncSwitches, writeVaultSyncSwitches } from "../vault-sync-switches.js";
5
5
  import { clearEngineStatus, engineFirstCycle, isSyncCoreVaultUid, removeEngineOwnership, syncCoreEngineHome, writeEngineHome, } from "./engine-home.js";
6
- import { SYNC_CORE_COMPANY_FULL_RELEASE, SYNC_CORE_RELEASE, companyFullModeAllowed, installedSyncCoreRelease, syncCoreBinaryPath, } from "./release.js";
6
+ import { SYNC_CORE_FULL_RELEASE, SYNC_CORE_RELEASE, fullModeAllowed, installedSyncCoreRelease, syncCoreBinaryPath, } from "./release.js";
7
7
  import { readSyncCoreSkips } from "./skip.js";
8
+ import { SYNC_RUST_ENGINE_ROLLOUT_FLAG } from "./flag.js";
8
9
  import { readSyncCoreRolloutState, syncCoreRolloutStatePath, writeSyncCoreRolloutState, } from "./rollout-state.js";
9
10
  function message(error) {
10
11
  return error instanceof Error ? error.message : String(error);
11
12
  }
12
- export function companyFullHold(vault, release) {
13
- if (vault.kind !== "company" || companyFullModeAllowed(release))
13
+ export function fullModeHold(vault, release) {
14
+ if (fullModeAllowed(release))
14
15
  return undefined;
15
- const needed = SYNC_CORE_COMPANY_FULL_RELEASE ? `engine release ${SYNC_CORE_COMPANY_FULL_RELEASE} or later` : "an engine release cleared for company vaults";
16
- return `company vaults move to the new sync engine with ${needed} (${release} installed); staying on the current sync`;
16
+ const needed = SYNC_CORE_FULL_RELEASE ? `engine release ${SYNC_CORE_FULL_RELEASE} or later` : "an engine release cleared for full sync";
17
+ return `${vault.kind} vaults move to the new sync engine with ${needed} (${release} installed); staying on the current sync`;
17
18
  }
18
19
  function localCompanies(hqRoot, memberships) {
19
20
  const manifest = readCompaniesManifestMap(hqRoot) ?? {};
@@ -78,7 +79,7 @@ export async function reconcileSyncCore(deps) {
78
79
  on = await deps.readRolloutFlag(company.uid);
79
80
  }
80
81
  catch (error) {
81
- on = previousByUid.has(company.uid);
82
+ on = previous?.rolloutFlagKey === SYNC_RUST_ENGINE_ROLLOUT_FLAG && previousByUid.has(company.uid);
82
83
  warnings.push(`${message(error)}; keeping ${company.slug} ${on ? "on the new engine" : "on the current sync"}`);
83
84
  }
84
85
  if (on) {
@@ -132,7 +133,7 @@ export async function reconcileSyncCore(deps) {
132
133
  const held = [];
133
134
  const engineRelease = installedRelease ?? release;
134
135
  for (let i = desired.length - 1; i >= 0; i -= 1) {
135
- const reason = companyFullHold(desired[i], engineRelease);
136
+ const reason = fullModeHold(desired[i], engineRelease);
136
137
  if (!reason)
137
138
  continue;
138
139
  held.unshift({ uid: desired[i].uid, reason });
@@ -200,6 +201,7 @@ export async function reconcileSyncCore(deps) {
200
201
  }
201
202
  const state = {
202
203
  version: 1,
204
+ rolloutFlagKey: SYNC_RUST_ENGINE_ROLLOUT_FLAG,
203
205
  readAt: at,
204
206
  release: installedRelease ?? release,
205
207
  binaryPath,
@@ -1,7 +1,7 @@
1
- export declare const SYNC_CORE_RELEASE = "v0.1.21";
2
- export declare const SYNC_CORE_COMPANY_FULL_RELEASE: string | null;
1
+ export declare const SYNC_CORE_RELEASE = "v0.1.23";
2
+ export declare const SYNC_CORE_FULL_RELEASE: string | null;
3
3
  export declare function compareSyncCoreReleases(a: string, b: string): number;
4
- export declare function companyFullModeAllowed(release: string): boolean;
4
+ export declare function fullModeAllowed(release: string): boolean;
5
5
  export declare const SYNC_CORE_DOWNLOAD_BASE = "https://indigoai-public.s3.amazonaws.com/hq-sync-core";
6
6
  export declare const SYNC_CORE_DOWNLOAD_BASE_ENV = "HQ_SYNC_CORE_DOWNLOAD_BASE";
7
7
  export declare function syncCoreBinaryPath(home?: string): string;
@@ -3,8 +3,8 @@ import * as fs from "node:fs";
3
3
  import * as os from "node:os";
4
4
  import * as path from "node:path";
5
5
  import { execFileSync } from "node:child_process";
6
- export const SYNC_CORE_RELEASE = "v0.1.21";
7
- export const SYNC_CORE_COMPANY_FULL_RELEASE = "v0.1.21";
6
+ export const SYNC_CORE_RELEASE = "v0.1.23";
7
+ export const SYNC_CORE_FULL_RELEASE = "v0.1.23";
8
8
  export function compareSyncCoreReleases(a, b) {
9
9
  const parse = (release) => release.replace(/^v/, "").split(".").map((part) => Number.parseInt(part, 10) || 0);
10
10
  const [x, y] = [parse(a), parse(b)];
@@ -15,8 +15,8 @@ export function compareSyncCoreReleases(a, b) {
15
15
  }
16
16
  return 0;
17
17
  }
18
- export function companyFullModeAllowed(release) {
19
- return SYNC_CORE_COMPANY_FULL_RELEASE !== null && compareSyncCoreReleases(release, SYNC_CORE_COMPANY_FULL_RELEASE) >= 0;
18
+ export function fullModeAllowed(release) {
19
+ return SYNC_CORE_FULL_RELEASE !== null && compareSyncCoreReleases(release, SYNC_CORE_FULL_RELEASE) >= 0;
20
20
  }
21
21
  export const SYNC_CORE_DOWNLOAD_BASE = "https://indigoai-public.s3.amazonaws.com/hq-sync-core";
22
22
  export const SYNC_CORE_DOWNLOAD_BASE_ENV = "HQ_SYNC_CORE_DOWNLOAD_BASE";
@@ -11,6 +11,7 @@ export interface RolloutVault {
11
11
  }
12
12
  export interface SyncCoreRolloutState {
13
13
  version: 1;
14
+ rolloutFlagKey?: string;
14
15
  readAt: string;
15
16
  release: string;
16
17
  binaryPath: string;
@@ -100,6 +100,7 @@ export function readSyncCoreRolloutState(home) {
100
100
  return null;
101
101
  return {
102
102
  version: 1,
103
+ ...(typeof obj.rolloutFlagKey === "string" ? { rolloutFlagKey: obj.rolloutFlagKey } : {}),
103
104
  readAt: obj.readAt,
104
105
  release: obj.release,
105
106
  binaryPath: obj.binaryPath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.345.178",
3
+ "version": "5.345.180",
4
4
  "description": "HQ by Indigo management CLI — modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -52,7 +52,7 @@
52
52
  "@aws-sdk/client-iot-data-plane": "^3.1096.0",
53
53
  "@aws-sdk/client-s3": "^3.1049.0",
54
54
  "@aws-sdk/dsql-signer": "^3.1146.0",
55
- "@indigoai-us/hq-cloud": "6.18.104",
55
+ "@indigoai-us/hq-cloud": "6.18.105",
56
56
  "@indigoai-us/hq-flags-client": "^0.1.2",
57
57
  "@modelcontextprotocol/sdk": "1.29.0",
58
58
  "@sentry/node": "^10.49.0",
@@ -114,11 +114,11 @@
114
114
  "*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}": "eslint --cache --no-error-on-unmatched-pattern"
115
115
  },
116
116
  "optionalDependencies": {
117
- "@indigoai-us/hq-hook-win32-x64": "5.345.178",
118
- "@indigoai-us/hq-hook-win32-arm64": "5.345.178",
119
- "@indigoai-us/hq-hook-darwin-x64": "5.345.178",
120
- "@indigoai-us/hq-hook-darwin-arm64": "5.345.178",
121
- "@indigoai-us/hq-hook-linux-x64": "5.345.178",
122
- "@indigoai-us/hq-hook-linux-arm64": "5.345.178"
117
+ "@indigoai-us/hq-hook-win32-x64": "5.345.180",
118
+ "@indigoai-us/hq-hook-win32-arm64": "5.345.180",
119
+ "@indigoai-us/hq-hook-darwin-x64": "5.345.180",
120
+ "@indigoai-us/hq-hook-darwin-arm64": "5.345.180",
121
+ "@indigoai-us/hq-hook-linux-x64": "5.345.180",
122
+ "@indigoai-us/hq-hook-linux-arm64": "5.345.180"
123
123
  }
124
124
  }