infinicode 2.8.91 → 2.8.92

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "infinicode",
3
- "version": "2.8.91",
3
+ "version": "2.8.92",
4
4
  "description": "OpenKernel — provider-agnostic AI execution kernel. Native coding agent + mission-driven execution runtime.",
5
5
  "type": "module",
6
6
  "main": "./dist/kernel/index.js",
@@ -1257,17 +1257,14 @@ class LiveKitPublisher:
1257
1257
  # — until it finished, so a short "Hello friend!" greeting could be
1258
1258
  # over and gone before we ever got a chance to subscribe to it.
1259
1259
  if audio_enabled:
1260
- self._tasks.append(asyncio.create_task(self._audio_loop()))
1260
+ self._tasks.append(asyncio.create_task(self._audio_retry_loop()))
1261
1261
  try:
1262
1262
  await asyncio.wait_for(self._mic_streaming.wait(), timeout=5.0)
1263
- await self.agent._report_pipeline(
1264
- "microphone_published", "ok", "Microphone track published with live PCM frames"
1265
- )
1266
1263
  except asyncio.TimeoutError:
1267
1264
  await self.agent._report_pipeline(
1268
1265
  "microphone_published", "blocked", "Microphone track published but PCM capture did not start"
1269
1266
  )
1270
- raise RuntimeError(f"microphone capture did not start for {self.agent.audio_capture_device}")
1267
+ logger.warning("microphone PCM is not ready; keeping the session alive while capture retries")
1271
1268
 
1272
1269
  if self._capture and self.video_source:
1273
1270
  self._tasks.append(asyncio.create_task(self._video_loop()))
@@ -1596,7 +1593,29 @@ class LiveKitPublisher:
1596
1593
  except Exception as e:
1597
1594
  logger.debug(f"preview motion sampling failed: {e}")
1598
1595
 
1599
- async def _audio_loop(self) -> None:
1596
+ async def _audio_retry_loop(self) -> None:
1597
+ """Keep microphone capture alive across transient ALSA ownership errors."""
1598
+ while not self._stop_event.is_set():
1599
+ try:
1600
+ await self._audio_loop()
1601
+ except asyncio.CancelledError:
1602
+ raise
1603
+ except Exception as e:
1604
+ logger.warning(
1605
+ f"audio_loop: capture failed on {self.agent.audio_capture_device}: {e}; retrying"
1606
+ )
1607
+ finally:
1608
+ mic = self._mic_capture
1609
+ self._mic_capture = None
1610
+ if mic is not None:
1611
+ await asyncio.to_thread(mic.stop)
1612
+ if not self._stop_event.is_set():
1613
+ try:
1614
+ await asyncio.wait_for(self._stop_event.wait(), timeout=1.0)
1615
+ except asyncio.TimeoutError:
1616
+ pass
1617
+
1618
+ async def _audio_loop(self) -> None:
1600
1619
  assert self.audio_source is not None
1601
1620
  mic = create_audio_capture(self.agent.audio_capture_device)
1602
1621
  if mic is None:
@@ -1642,7 +1661,12 @@ class LiveKitPublisher:
1642
1661
  data=chunk, sample_rate=48000, num_channels=1, samples_per_channel=samples_per_frame,
1643
1662
  )
1644
1663
  await self.audio_source.capture_frame(frame)
1645
- self._mic_streaming.set()
1664
+ if not self._mic_streaming.is_set():
1665
+ self._mic_streaming.set()
1666
+ await self.agent._report_pipeline(
1667
+ "microphone_published", "ok",
1668
+ "Microphone track published with live PCM frames",
1669
+ )
1646
1670
  p = audioop.max(chunk, 2)
1647
1671
  _diag_peak = max(_diag_peak, p)
1648
1672
  _diag_count += 1
@@ -1852,7 +1876,7 @@ class AlsaAudioCapture(AudioCapture):
1852
1876
  "-f", "S16_LE", "-r", "48000", "-c", "1",
1853
1877
  ],
1854
1878
  stdout=subprocess.PIPE,
1855
- stderr=subprocess.DEVNULL,
1879
+ stderr=subprocess.PIPE,
1856
1880
  )
1857
1881
  if self.process.stdout is None:
1858
1882
  raise RuntimeError("arecord did not provide a PCM stream")
@@ -1865,7 +1889,10 @@ class AlsaAudioCapture(AudioCapture):
1865
1889
  while len(self.buffer) < bytes_needed:
1866
1890
  chunk = self.process.stdout.read(bytes_needed - len(self.buffer))
1867
1891
  if not chunk:
1868
- raise OSError(f"ALSA capture stopped on {self.alsa_device}")
1892
+ detail = ""
1893
+ if self.process.stderr is not None:
1894
+ detail = self.process.stderr.read().decode("utf-8", errors="replace").strip()
1895
+ raise OSError(f"ALSA capture stopped on {self.alsa_device}: {detail or 'no PCM data'}")
1869
1896
  self.buffer.extend(chunk)
1870
1897
  data = bytes(self.buffer[:bytes_needed])
1871
1898
  del self.buffer[:bytes_needed]
@@ -1883,6 +1910,7 @@ class AlsaAudioCapture(AudioCapture):
1883
1910
  self.process.wait(timeout=2)
1884
1911
  except Exception:
1885
1912
  self.process.kill()
1913
+ self.process.wait(timeout=1)
1886
1914
 
1887
1915
 
1888
1916
  class PyAudioCapture(AudioCapture):