@integrity-labs/agt-cli 0.28.808 → 0.28.809

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.
@@ -100,7 +100,7 @@ async function spawnPairSession(session) {
100
100
  return { ok: true };
101
101
  } catch {
102
102
  }
103
- const { resolveClaudeBinary } = await import("./persistent-session-Q23JIYBR.js");
103
+ const { resolveClaudeBinary } = await import("./persistent-session-QYP6FYFI.js");
104
104
  const claudeBin = resolveClaudeBinary();
105
105
  const pairEnv = {
106
106
  ...process.env,
@@ -373,4 +373,4 @@ export {
373
373
  startClaudePair,
374
374
  submitClaudePairCode
375
375
  };
376
- //# sourceMappingURL=claude-pair-runtime-FUT7SLRJ.js.map
376
+ //# sourceMappingURL=claude-pair-runtime-P4R5WMUQ.js.map
@@ -58,7 +58,7 @@ import {
58
58
  safeWriteJsonAtomic,
59
59
  setConfigHash,
60
60
  tripClass
61
- } from "../chunk-LLR42RME.js";
61
+ } from "../chunk-2Z5SJMME.js";
62
62
  import {
63
63
  getProjectDir as getProjectDir2,
64
64
  getReadyTasks,
@@ -123,7 +123,7 @@ import {
123
123
  takeZombieDetection,
124
124
  toOpencodeModel,
125
125
  writeEgressAllowlist
126
- } from "../chunk-TLPQULYH.js";
126
+ } from "../chunk-5TXNXEOF.js";
127
127
  import {
128
128
  ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
129
129
  AnchorSessionClient,
@@ -215,7 +215,7 @@ import {
215
215
  subagentActivityAgeSeconds,
216
216
  sumTranscriptUsageInWindow,
217
217
  transcriptActivityAgeSeconds
218
- } from "../chunk-SWUBTUYM.js";
218
+ } from "../chunk-JNHL4KS4.js";
219
219
  import {
220
220
  reapOrphanChannelMcps
221
221
  } from "../chunk-XWVM4KPK.js";
@@ -12918,7 +12918,7 @@ var pendingDayRolloverReset = /* @__PURE__ */ new Set();
12918
12918
  var dayRolloverInboundHold = /* @__PURE__ */ new Map();
12919
12919
  var INBOUND_HOLD_EPISODE_GAP_MS = 12e4;
12920
12920
  async function channelInboundActivityAgeSecondsFor(codeName) {
12921
- const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-T2I27SCG.js");
12921
+ const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-SL2MZ3WT.js");
12922
12922
  const newest = newestPendingInboundActivityMtimeMs(dirname10(paneLogPath(codeName)));
12923
12923
  if (newest === null) return null;
12924
12924
  return Math.max(0, Math.floor((Date.now() - newest) / 1e3));
@@ -13578,7 +13578,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
13578
13578
  var lastVersionCheckAt = 0;
13579
13579
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
13580
13580
  var lastResponsivenessProbeAt = 0;
13581
- var agtCliVersion = true ? "0.28.808" : "dev";
13581
+ var agtCliVersion = true ? "0.28.809" : "dev";
13582
13582
  function resolveBrewPath(execFileSync2) {
13583
13583
  try {
13584
13584
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -15125,7 +15125,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
15125
15125
  if (codeNames.length === 0) return;
15126
15126
  void (async () => {
15127
15127
  try {
15128
- const { collectDiagnostics } = await import("../persistent-session-Q23JIYBR.js");
15128
+ const { collectDiagnostics } = await import("../persistent-session-QYP6FYFI.js");
15129
15129
  await api.post("/host/heartbeat", {
15130
15130
  host_id: hostId,
15131
15131
  agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -15266,7 +15266,7 @@ async function pollCycleInner() {
15266
15266
  }
15267
15267
  try {
15268
15268
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
15269
- const { collectDiagnostics } = await import("../persistent-session-Q23JIYBR.js");
15269
+ const { collectDiagnostics } = await import("../persistent-session-QYP6FYFI.js");
15270
15270
  const diagCodeNames = [...agentState.persistentSessionAgents];
15271
15271
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor) : void 0;
15272
15272
  let tailscaleHostname;
@@ -15435,7 +15435,7 @@ async function pollCycleInner() {
15435
15435
  collectPanelessActivityProbes,
15436
15436
  getResponsivenessIntervalMs,
15437
15437
  occupancyQualificationClassifications
15438
- } = await import("../responsiveness-probe-T2I27SCG.js");
15438
+ } = await import("../responsiveness-probe-SL2MZ3WT.js");
15439
15439
  const probeIntervalMs = getResponsivenessIntervalMs();
15440
15440
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
15441
15441
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -15552,7 +15552,7 @@ async function pollCycleInner() {
15552
15552
  collectResponsivenessProbes,
15553
15553
  livePendingInboundOldestAgeSeconds,
15554
15554
  parkPendingInbound
15555
- } = await import("../responsiveness-probe-T2I27SCG.js");
15555
+ } = await import("../responsiveness-probe-SL2MZ3WT.js");
15556
15556
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-NDP36U7O.js");
15557
15557
  const wedgeNow = /* @__PURE__ */ new Date();
15558
15558
  const liveAgents = agentState.persistentSessionAgents;
@@ -15708,7 +15708,7 @@ async function pollCycleInner() {
15708
15708
  }
15709
15709
  try {
15710
15710
  const { scrapeMcpFailedBannerCount } = await import("../pane-mcp-banner-scraper-JA437JIB.js");
15711
- const { probeSessionAuth } = await import("../session-auth-dead-O7DBSJ3W.js");
15711
+ const { probeSessionAuth } = await import("../session-auth-dead-VZWGAV5K.js");
15712
15712
  const observations = [];
15713
15713
  const pendingCacheCommits = [];
15714
15714
  const modelApiErrorReportingOn = hostFlagStore().getBoolean("model-api-error-reporting");
@@ -19868,7 +19868,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
19868
19868
  void api.post("/host/restart-ack", { host_id: hostId, agent_id: agentId, restart_requested_at: requestedAt }).catch((err) => log(`[restart-lane] ack failed for '${codeName}': ${err.message}`));
19869
19869
  void (async () => {
19870
19870
  try {
19871
- const { collectDiagnostics } = await import("../persistent-session-Q23JIYBR.js");
19871
+ const { collectDiagnostics } = await import("../persistent-session-QYP6FYFI.js");
19872
19872
  await api.post("/host/heartbeat", {
19873
19873
  host_id: hostId,
19874
19874
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -19914,7 +19914,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
19914
19914
  }
19915
19915
  try {
19916
19916
  const hostId = await getHostId();
19917
- const { collectDiagnostics } = await import("../persistent-session-Q23JIYBR.js");
19917
+ const { collectDiagnostics } = await import("../persistent-session-QYP6FYFI.js");
19918
19918
  await api.post("/host/heartbeat", {
19919
19919
  host_id: hostId,
19920
19920
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -20550,7 +20550,7 @@ async function processClaudePairSessions(agents) {
20550
20550
  killPairSession,
20551
20551
  pairTmuxSession,
20552
20552
  finalizeClaudePairOnboarding
20553
- } = await import("../claude-pair-runtime-FUT7SLRJ.js");
20553
+ } = await import("../claude-pair-runtime-P4R5WMUQ.js");
20554
20554
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
20555
20555
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
20556
20556
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -33806,7 +33806,15 @@ var INTEGRATION_REGISTRY = [
33806
33806
  // integration_rate_cards; this only declares the model + meter.
33807
33807
  premium: {
33808
33808
  pricing: "usage",
33809
- note: "Billed per minute of real-time voice conversation.",
33809
+ // THE OPT-IN AUTHORISES BOTH METERS, SO THE NOTE NAMES BOTH (ENG-9913).
33810
+ // This string is the point of sale — it renders in the add-integration
33811
+ // dialog an org admin reads before opting in — and it described only
33812
+ // `voice_session` while the same opt-in, and the same $50 pool, had also
33813
+ // covered Ninja Notes cloud transcription since ENG-9870. "Of audio"
33814
+ // rather than "of meeting" is load-bearing: the two capture sources post
33815
+ // independently, so an hour-long meeting on default settings spends two
33816
+ // audio-hours (ADR-0063 §2a).
33817
+ note: "Billed per minute of real-time voice conversation, and per hour of audio for Ninja Notes cloud transcription \u2014 both against the same monthly budget.",
33810
33818
  meters: [
33811
33819
  { event_type: "voice_session", unit: "minute" },
33812
33820
  // ENG-9870. Ninja Notes cloud transcription, on the SAME definition id
@@ -33818,16 +33826,24 @@ var INTEGRATION_REGISTRY = [
33818
33826
  // `audio_hour`, not `minute`. The vendor prices per hour and the sell
33819
33827
  // price is per hour, so hours store exactly (50 minor) where minutes
33820
33828
  // would be 0.8333 repeating; `integration_usage_events.units` is
33821
- // `numeric`, so a 30-minute meeting records 0.5 units precisely. This
33829
+ // `numeric`, so 30 minutes of audio records 0.5 units precisely. This
33822
33830
  // is the answer to ENG-9773 §10.1's open question about whether hours
33823
33831
  // convert to minutes or the vocabulary gains hours.
33824
33832
  //
33825
- // NOTHING RECORDS THIS EVENT YET. Metering belongs at the chokepoint
33826
- // where audio is actually sent, and ENG-9868 left the transport
33827
- // injected with no implementation. The rate card exists first
33828
- // deliberately: a billable event with no USD rate makes
33829
- // `evaluateUsageBudget` throw, and on a SHARED pool that would break
33830
- // Voice Chat's budget check, not just this one.
33833
+ // AN AUDIO-HOUR IS NOT A MEETING-HOUR (ENG-9913, Brad, 2026-09-03). The
33834
+ // Mac captures system audio and the microphone as two independent
33835
+ // pipelines that post separately, and both default on, so the ordinary
33836
+ // meeting records two units per wall-clock hour. That is deliberate: the
33837
+ // audio-hour is what we buy, since xAI bills on the duration uploaded.
33838
+ // Pricing per meeting-hour would have varied the markup with a capture
33839
+ // toggle and could only have been built on a client's assertion about
33840
+ // how many pipelines were live. See ADR-0063 §2a.
33841
+ //
33842
+ // THE CHOKEPOINT EXISTS NOW — `POST /notes/transcribe` (ENG-9913)
33843
+ // reserves against this meter before the vendor call, from the bytes it
33844
+ // received. The rate card still had to land first, in ENG-9870: a
33845
+ // billable event with no USD rate makes `evaluateUsageBudget` throw, and
33846
+ // on a SHARED pool that would break Voice Chat's budget check too.
33831
33847
  { event_type: "transcription", unit: "audio_hour" }
33832
33848
  ]
33833
33849
  }
@@ -34724,7 +34740,18 @@ var INTEGRATION_REGISTRY = [
34724
34740
  // premium surface — is the structurally right answer and the follow-up.
34725
34741
  premium: {
34726
34742
  pricing: "included",
34727
- note: "Notes are included in your plan. Transcription runs on your own Mac and no platform summariser runs, so nothing is metered per note \u2014 the agent turn that reads it is billed as agent-hours like any other. Live meeting advising is the one exception: it is turned on separately, and billed per minute the advisor is armed."
34743
+ // "THE ONE EXCEPTION" BECAME TWO (ENG-9913). Cloud transcription is the
34744
+ // second metered capability under this integration, and this sentence was
34745
+ // true only for as long as its flag stayed off — it would have become
34746
+ // false on arming, silently, in a consent dialog. Both exceptions are
34747
+ // written the same way, and both are still off until someone turns them
34748
+ // on, so the sentence is accurate before and after the rollout.
34749
+ //
34750
+ // "Per hour of AUDIO" is deliberate here too: the two capture sources post
34751
+ // independently, so an hour-long meeting on default settings spends two
34752
+ // audio-hours (ADR-0063 §2a). The Mac's own caption carries the fuller
34753
+ // explanation, at the point where the person choosing it can read it.
34754
+ note: "Notes are included in your plan. Transcription runs on your own Mac by default and no platform summariser runs, so nothing is metered per note \u2014 the agent turn that reads it is billed as agent-hours like any other. Two capabilities are turned on separately and billed when they are: live meeting advising, per minute the advisor is armed; and cloud transcription, per hour of audio."
34728
34755
  }
34729
34756
  },
34730
34757
  {
@@ -37671,6 +37698,18 @@ var FLAG_REGISTRY = [
37671
37698
  // agent-hours-billing-enabled.
37672
37699
  sensitive: true
37673
37700
  },
37701
+ {
37702
+ key: "cloud-speech-to-text",
37703
+ description: 'Cloud transcription in Ninja Notes (ENG-9913). When ON for an org, a person recording a meeting on the Mac may choose to have each capture window transcribed by xAI instead of on-device: the window is POSTed to /notes/transcribe, proxied to xAI with the platform key, metered as grok-voice/transcription per audio_hour, and the text comes back with no speaker labels. THIS IS OUR ROLLOUT GATE AND IT IS THE THIRD KEY, NOT THE FIRST. Three things must all be true before a window leaves the Mac: the org holds the grok-voice premium opt-in (authorisation, resolveCloudSttEntitlement), the user has elected it per-install (consent, CaptureSettings.cloudTranscriptionEnabled), and this flag is on (our own readiness). Without this flag the first two alone were sufficient, which is why it exists: resolveCloudSttEntitlement reports enabled for every org that ever bought Voice Chat, so the route would go live for all of them the moment it deployed. It is what finally lets the server emit `not_available` \u2014 CloudTranscriptionRefusal.notRolledOut has shipped since ENG-9869 carrying the sentence "Cloud transcription is not available for your organisation yet" with nothing able to produce it. Boolean gate; ships dark. OFF NEVER COSTS THE RECORDING. An org without the flag \u2014 or a flag read that fails \u2014 transcribes on-device exactly as before; the microphone still starts and the note is still transcribed and delivered. Same direction as live-meeting-advisor and for the same reason: the user pressed Record and a recording is what they are owed. It is also what makes rollback cheap, because flipping this off mid-flight degrades in-progress meetings to on-device rather than breaking them. Deliberately NO envVar. This is an org-scoped gate, and an envVar is highest-precedence per ADR-0022 \u2014 a literal in sst.config.ts would pin every stage and make the admin control do nothing, the ENG-8303 class exactly. No `since`: the reader is the API route, not a host-side agt-cli consumer, so this does not depend on fleet convergence. The Mac client is a separate fleet that reports no version at all, which is precisely why the gate has to live server-side.',
37704
+ flagType: "boolean",
37705
+ defaultValue: false,
37706
+ // Turning it ON starts sending meeting audio — INCLUDING the other side of
37707
+ // the call, whose participants have not been asked — to a third-party
37708
+ // vendor, and arms a per-audio-hour meter against the org's shared
37709
+ // grok-voice budget. Either half alone would warrant the confirm ADR-0022
37710
+ // §4 reserves for deliberate, audited flips.
37711
+ sensitive: true
37712
+ },
37674
37713
  {
37675
37714
  key: "first-party-token-refusal",
37676
37715
  description: "ENG-8972 AC4 / ADR-0076 decision 2 \u2014 whether `resolveUserId` (packages/api/src/middleware/auth.ts) REFUSES a bearer token carrying a first-party marker that is foreign to the host-exchange lane. SUPABASE_JWT_SECRET is symmetric and signs twelve distinct token families; the generic Bearer door has always admitted any of them that mints a uuid-shaped `sub`, so a token minted for one purpose authenticates as a user for every purpose. off = the capture never runs (no log, no refusal). shadow = record the population an enforcement flip would change, on BOTH admitting lanes (lane=gotrue, lane=sub-claim), and refuse nothing \u2014 this is exactly the behaviour compiled into prod today. enforce = return null with reason=foreign-first-party-marker, above the strategy split, so the token reaches neither GoTrue nor the sub-claim lane. DEFAULT IS `shadow`, NOT `off`, and the divergence is deliberate \u2014 the shadow lines have been live in prod since 68863974b, and they are the soak instrument. Defaulting to `off` would silently switch off the measurement this flag exists to act on: the channel-quarantine-mode landmine (ENG-6252) in its exact shape. A flag-read failure degrades to `shadow` for the same reason it fails open elsewhere on this path: this is the platform front door, and a `feature_flags` blip must not start refusing the fleet. Deliberately NO envVar. The consumer is the API Lambda, whose environment block is at the AWS 4KB cap (ENG-7972), and an envVar is highest-precedence per ADR-0022 \u2014 a literal in sst.config.ts would pin every stage and make the admin control do nothing (ENG-8303). The flag value IS the kill switch: a flip back to shadow takes effect within the ~60s evaluation cache, with no deploy.",
package/dist/mcp/index.js CHANGED
@@ -41350,7 +41350,15 @@ var INTEGRATION_REGISTRY = [
41350
41350
  // integration_rate_cards; this only declares the model + meter.
41351
41351
  premium: {
41352
41352
  pricing: "usage",
41353
- note: "Billed per minute of real-time voice conversation.",
41353
+ // THE OPT-IN AUTHORISES BOTH METERS, SO THE NOTE NAMES BOTH (ENG-9913).
41354
+ // This string is the point of sale — it renders in the add-integration
41355
+ // dialog an org admin reads before opting in — and it described only
41356
+ // `voice_session` while the same opt-in, and the same $50 pool, had also
41357
+ // covered Ninja Notes cloud transcription since ENG-9870. "Of audio"
41358
+ // rather than "of meeting" is load-bearing: the two capture sources post
41359
+ // independently, so an hour-long meeting on default settings spends two
41360
+ // audio-hours (ADR-0063 §2a).
41361
+ note: "Billed per minute of real-time voice conversation, and per hour of audio for Ninja Notes cloud transcription \u2014 both against the same monthly budget.",
41354
41362
  meters: [
41355
41363
  { event_type: "voice_session", unit: "minute" },
41356
41364
  // ENG-9870. Ninja Notes cloud transcription, on the SAME definition id
@@ -41362,16 +41370,24 @@ var INTEGRATION_REGISTRY = [
41362
41370
  // `audio_hour`, not `minute`. The vendor prices per hour and the sell
41363
41371
  // price is per hour, so hours store exactly (50 minor) where minutes
41364
41372
  // would be 0.8333 repeating; `integration_usage_events.units` is
41365
- // `numeric`, so a 30-minute meeting records 0.5 units precisely. This
41373
+ // `numeric`, so 30 minutes of audio records 0.5 units precisely. This
41366
41374
  // is the answer to ENG-9773 §10.1's open question about whether hours
41367
41375
  // convert to minutes or the vocabulary gains hours.
41368
41376
  //
41369
- // NOTHING RECORDS THIS EVENT YET. Metering belongs at the chokepoint
41370
- // where audio is actually sent, and ENG-9868 left the transport
41371
- // injected with no implementation. The rate card exists first
41372
- // deliberately: a billable event with no USD rate makes
41373
- // `evaluateUsageBudget` throw, and on a SHARED pool that would break
41374
- // Voice Chat's budget check, not just this one.
41377
+ // AN AUDIO-HOUR IS NOT A MEETING-HOUR (ENG-9913, Brad, 2026-09-03). The
41378
+ // Mac captures system audio and the microphone as two independent
41379
+ // pipelines that post separately, and both default on, so the ordinary
41380
+ // meeting records two units per wall-clock hour. That is deliberate: the
41381
+ // audio-hour is what we buy, since xAI bills on the duration uploaded.
41382
+ // Pricing per meeting-hour would have varied the markup with a capture
41383
+ // toggle and could only have been built on a client's assertion about
41384
+ // how many pipelines were live. See ADR-0063 §2a.
41385
+ //
41386
+ // THE CHOKEPOINT EXISTS NOW — `POST /notes/transcribe` (ENG-9913)
41387
+ // reserves against this meter before the vendor call, from the bytes it
41388
+ // received. The rate card still had to land first, in ENG-9870: a
41389
+ // billable event with no USD rate makes `evaluateUsageBudget` throw, and
41390
+ // on a SHARED pool that would break Voice Chat's budget check too.
41375
41391
  { event_type: "transcription", unit: "audio_hour" }
41376
41392
  ]
41377
41393
  }
@@ -42268,7 +42284,18 @@ var INTEGRATION_REGISTRY = [
42268
42284
  // premium surface — is the structurally right answer and the follow-up.
42269
42285
  premium: {
42270
42286
  pricing: "included",
42271
- note: "Notes are included in your plan. Transcription runs on your own Mac and no platform summariser runs, so nothing is metered per note \u2014 the agent turn that reads it is billed as agent-hours like any other. Live meeting advising is the one exception: it is turned on separately, and billed per minute the advisor is armed."
42287
+ // "THE ONE EXCEPTION" BECAME TWO (ENG-9913). Cloud transcription is the
42288
+ // second metered capability under this integration, and this sentence was
42289
+ // true only for as long as its flag stayed off — it would have become
42290
+ // false on arming, silently, in a consent dialog. Both exceptions are
42291
+ // written the same way, and both are still off until someone turns them
42292
+ // on, so the sentence is accurate before and after the rollout.
42293
+ //
42294
+ // "Per hour of AUDIO" is deliberate here too: the two capture sources post
42295
+ // independently, so an hour-long meeting on default settings spends two
42296
+ // audio-hours (ADR-0063 §2a). The Mac's own caption carries the fuller
42297
+ // explanation, at the point where the person choosing it can read it.
42298
+ note: "Notes are included in your plan. Transcription runs on your own Mac by default and no platform summariser runs, so nothing is metered per note \u2014 the agent turn that reads it is billed as agent-hours like any other. Two capabilities are turned on separately and billed when they are: live meeting advising, per minute the advisor is armed; and cloud transcription, per hour of audio."
42272
42299
  }
42273
42300
  },
42274
42301
  {
@@ -44776,6 +44803,18 @@ var FLAG_REGISTRY = [
44776
44803
  // agent-hours-billing-enabled.
44777
44804
  sensitive: true
44778
44805
  },
44806
+ {
44807
+ key: "cloud-speech-to-text",
44808
+ description: 'Cloud transcription in Ninja Notes (ENG-9913). When ON for an org, a person recording a meeting on the Mac may choose to have each capture window transcribed by xAI instead of on-device: the window is POSTed to /notes/transcribe, proxied to xAI with the platform key, metered as grok-voice/transcription per audio_hour, and the text comes back with no speaker labels. THIS IS OUR ROLLOUT GATE AND IT IS THE THIRD KEY, NOT THE FIRST. Three things must all be true before a window leaves the Mac: the org holds the grok-voice premium opt-in (authorisation, resolveCloudSttEntitlement), the user has elected it per-install (consent, CaptureSettings.cloudTranscriptionEnabled), and this flag is on (our own readiness). Without this flag the first two alone were sufficient, which is why it exists: resolveCloudSttEntitlement reports enabled for every org that ever bought Voice Chat, so the route would go live for all of them the moment it deployed. It is what finally lets the server emit `not_available` \u2014 CloudTranscriptionRefusal.notRolledOut has shipped since ENG-9869 carrying the sentence "Cloud transcription is not available for your organisation yet" with nothing able to produce it. Boolean gate; ships dark. OFF NEVER COSTS THE RECORDING. An org without the flag \u2014 or a flag read that fails \u2014 transcribes on-device exactly as before; the microphone still starts and the note is still transcribed and delivered. Same direction as live-meeting-advisor and for the same reason: the user pressed Record and a recording is what they are owed. It is also what makes rollback cheap, because flipping this off mid-flight degrades in-progress meetings to on-device rather than breaking them. Deliberately NO envVar. This is an org-scoped gate, and an envVar is highest-precedence per ADR-0022 \u2014 a literal in sst.config.ts would pin every stage and make the admin control do nothing, the ENG-8303 class exactly. No `since`: the reader is the API route, not a host-side agt-cli consumer, so this does not depend on fleet convergence. The Mac client is a separate fleet that reports no version at all, which is precisely why the gate has to live server-side.',
44809
+ flagType: "boolean",
44810
+ defaultValue: false,
44811
+ // Turning it ON starts sending meeting audio — INCLUDING the other side of
44812
+ // the call, whose participants have not been asked — to a third-party
44813
+ // vendor, and arms a per-audio-hour meter against the org's shared
44814
+ // grok-voice budget. Either half alone would warrant the confirm ADR-0022
44815
+ // §4 reserves for deliberate, audited flips.
44816
+ sensitive: true
44817
+ },
44779
44818
  {
44780
44819
  key: "first-party-token-refusal",
44781
44820
  description: "ENG-8972 AC4 / ADR-0076 decision 2 \u2014 whether `resolveUserId` (packages/api/src/middleware/auth.ts) REFUSES a bearer token carrying a first-party marker that is foreign to the host-exchange lane. SUPABASE_JWT_SECRET is symmetric and signs twelve distinct token families; the generic Bearer door has always admitted any of them that mints a uuid-shaped `sub`, so a token minted for one purpose authenticates as a user for every purpose. off = the capture never runs (no log, no refusal). shadow = record the population an enforcement flip would change, on BOTH admitting lanes (lane=gotrue, lane=sub-claim), and refuse nothing \u2014 this is exactly the behaviour compiled into prod today. enforce = return null with reason=foreign-first-party-marker, above the strategy split, so the token reaches neither GoTrue nor the sub-claim lane. DEFAULT IS `shadow`, NOT `off`, and the divergence is deliberate \u2014 the shadow lines have been live in prod since 68863974b, and they are the soak instrument. Defaulting to `off` would silently switch off the measurement this flag exists to act on: the channel-quarantine-mode landmine (ENG-6252) in its exact shape. A flag-read failure degrades to `shadow` for the same reason it fails open elsewhere on this path: this is the platform front door, and a `feature_flags` blip must not start refusing the fleet. Deliberately NO envVar. The consumer is the API Lambda, whose environment block is at the AWS 4KB cap (ENG-7972), and an envVar is highest-precedence per ADR-0022 \u2014 a literal in sst.config.ts would pin every stage and make the admin control do nothing (ENG-8303). The flag value IS the kill switch: a flip back to shadow takes effect within the ~60s evaluation cache, with no deploy.",
@@ -39990,7 +39990,15 @@ var INTEGRATION_REGISTRY = [
39990
39990
  // integration_rate_cards; this only declares the model + meter.
39991
39991
  premium: {
39992
39992
  pricing: "usage",
39993
- note: "Billed per minute of real-time voice conversation.",
39993
+ // THE OPT-IN AUTHORISES BOTH METERS, SO THE NOTE NAMES BOTH (ENG-9913).
39994
+ // This string is the point of sale — it renders in the add-integration
39995
+ // dialog an org admin reads before opting in — and it described only
39996
+ // `voice_session` while the same opt-in, and the same $50 pool, had also
39997
+ // covered Ninja Notes cloud transcription since ENG-9870. "Of audio"
39998
+ // rather than "of meeting" is load-bearing: the two capture sources post
39999
+ // independently, so an hour-long meeting on default settings spends two
40000
+ // audio-hours (ADR-0063 §2a).
40001
+ note: "Billed per minute of real-time voice conversation, and per hour of audio for Ninja Notes cloud transcription \u2014 both against the same monthly budget.",
39994
40002
  meters: [
39995
40003
  { event_type: "voice_session", unit: "minute" },
39996
40004
  // ENG-9870. Ninja Notes cloud transcription, on the SAME definition id
@@ -40002,16 +40010,24 @@ var INTEGRATION_REGISTRY = [
40002
40010
  // `audio_hour`, not `minute`. The vendor prices per hour and the sell
40003
40011
  // price is per hour, so hours store exactly (50 minor) where minutes
40004
40012
  // would be 0.8333 repeating; `integration_usage_events.units` is
40005
- // `numeric`, so a 30-minute meeting records 0.5 units precisely. This
40013
+ // `numeric`, so 30 minutes of audio records 0.5 units precisely. This
40006
40014
  // is the answer to ENG-9773 §10.1's open question about whether hours
40007
40015
  // convert to minutes or the vocabulary gains hours.
40008
40016
  //
40009
- // NOTHING RECORDS THIS EVENT YET. Metering belongs at the chokepoint
40010
- // where audio is actually sent, and ENG-9868 left the transport
40011
- // injected with no implementation. The rate card exists first
40012
- // deliberately: a billable event with no USD rate makes
40013
- // `evaluateUsageBudget` throw, and on a SHARED pool that would break
40014
- // Voice Chat's budget check, not just this one.
40017
+ // AN AUDIO-HOUR IS NOT A MEETING-HOUR (ENG-9913, Brad, 2026-09-03). The
40018
+ // Mac captures system audio and the microphone as two independent
40019
+ // pipelines that post separately, and both default on, so the ordinary
40020
+ // meeting records two units per wall-clock hour. That is deliberate: the
40021
+ // audio-hour is what we buy, since xAI bills on the duration uploaded.
40022
+ // Pricing per meeting-hour would have varied the markup with a capture
40023
+ // toggle and could only have been built on a client's assertion about
40024
+ // how many pipelines were live. See ADR-0063 §2a.
40025
+ //
40026
+ // THE CHOKEPOINT EXISTS NOW — `POST /notes/transcribe` (ENG-9913)
40027
+ // reserves against this meter before the vendor call, from the bytes it
40028
+ // received. The rate card still had to land first, in ENG-9870: a
40029
+ // billable event with no USD rate makes `evaluateUsageBudget` throw, and
40030
+ // on a SHARED pool that would break Voice Chat's budget check too.
40015
40031
  { event_type: "transcription", unit: "audio_hour" }
40016
40032
  ]
40017
40033
  }
@@ -40908,7 +40924,18 @@ var INTEGRATION_REGISTRY = [
40908
40924
  // premium surface — is the structurally right answer and the follow-up.
40909
40925
  premium: {
40910
40926
  pricing: "included",
40911
- note: "Notes are included in your plan. Transcription runs on your own Mac and no platform summariser runs, so nothing is metered per note \u2014 the agent turn that reads it is billed as agent-hours like any other. Live meeting advising is the one exception: it is turned on separately, and billed per minute the advisor is armed."
40927
+ // "THE ONE EXCEPTION" BECAME TWO (ENG-9913). Cloud transcription is the
40928
+ // second metered capability under this integration, and this sentence was
40929
+ // true only for as long as its flag stayed off — it would have become
40930
+ // false on arming, silently, in a consent dialog. Both exceptions are
40931
+ // written the same way, and both are still off until someone turns them
40932
+ // on, so the sentence is accurate before and after the rollout.
40933
+ //
40934
+ // "Per hour of AUDIO" is deliberate here too: the two capture sources post
40935
+ // independently, so an hour-long meeting on default settings spends two
40936
+ // audio-hours (ADR-0063 §2a). The Mac's own caption carries the fuller
40937
+ // explanation, at the point where the person choosing it can read it.
40938
+ note: "Notes are included in your plan. Transcription runs on your own Mac by default and no platform summariser runs, so nothing is metered per note \u2014 the agent turn that reads it is billed as agent-hours like any other. Two capabilities are turned on separately and billed when they are: live meeting advising, per minute the advisor is armed; and cloud transcription, per hour of audio."
40912
40939
  }
40913
40940
  },
40914
40941
  {
@@ -43395,6 +43422,18 @@ var FLAG_REGISTRY = [
43395
43422
  // agent-hours-billing-enabled.
43396
43423
  sensitive: true
43397
43424
  },
43425
+ {
43426
+ key: "cloud-speech-to-text",
43427
+ description: 'Cloud transcription in Ninja Notes (ENG-9913). When ON for an org, a person recording a meeting on the Mac may choose to have each capture window transcribed by xAI instead of on-device: the window is POSTed to /notes/transcribe, proxied to xAI with the platform key, metered as grok-voice/transcription per audio_hour, and the text comes back with no speaker labels. THIS IS OUR ROLLOUT GATE AND IT IS THE THIRD KEY, NOT THE FIRST. Three things must all be true before a window leaves the Mac: the org holds the grok-voice premium opt-in (authorisation, resolveCloudSttEntitlement), the user has elected it per-install (consent, CaptureSettings.cloudTranscriptionEnabled), and this flag is on (our own readiness). Without this flag the first two alone were sufficient, which is why it exists: resolveCloudSttEntitlement reports enabled for every org that ever bought Voice Chat, so the route would go live for all of them the moment it deployed. It is what finally lets the server emit `not_available` \u2014 CloudTranscriptionRefusal.notRolledOut has shipped since ENG-9869 carrying the sentence "Cloud transcription is not available for your organisation yet" with nothing able to produce it. Boolean gate; ships dark. OFF NEVER COSTS THE RECORDING. An org without the flag \u2014 or a flag read that fails \u2014 transcribes on-device exactly as before; the microphone still starts and the note is still transcribed and delivered. Same direction as live-meeting-advisor and for the same reason: the user pressed Record and a recording is what they are owed. It is also what makes rollback cheap, because flipping this off mid-flight degrades in-progress meetings to on-device rather than breaking them. Deliberately NO envVar. This is an org-scoped gate, and an envVar is highest-precedence per ADR-0022 \u2014 a literal in sst.config.ts would pin every stage and make the admin control do nothing, the ENG-8303 class exactly. No `since`: the reader is the API route, not a host-side agt-cli consumer, so this does not depend on fleet convergence. The Mac client is a separate fleet that reports no version at all, which is precisely why the gate has to live server-side.',
43428
+ flagType: "boolean",
43429
+ defaultValue: false,
43430
+ // Turning it ON starts sending meeting audio — INCLUDING the other side of
43431
+ // the call, whose participants have not been asked — to a third-party
43432
+ // vendor, and arms a per-audio-hour meter against the org's shared
43433
+ // grok-voice budget. Either half alone would warrant the confirm ADR-0022
43434
+ // §4 reserves for deliberate, audited flips.
43435
+ sensitive: true
43436
+ },
43398
43437
  {
43399
43438
  key: "first-party-token-refusal",
43400
43439
  description: "ENG-8972 AC4 / ADR-0076 decision 2 \u2014 whether `resolveUserId` (packages/api/src/middleware/auth.ts) REFUSES a bearer token carrying a first-party marker that is foreign to the host-exchange lane. SUPABASE_JWT_SECRET is symmetric and signs twelve distinct token families; the generic Bearer door has always admitted any of them that mints a uuid-shaped `sub`, so a token minted for one purpose authenticates as a user for every purpose. off = the capture never runs (no log, no refusal). shadow = record the population an enforcement flip would change, on BOTH admitting lanes (lane=gotrue, lane=sub-claim), and refuse nothing \u2014 this is exactly the behaviour compiled into prod today. enforce = return null with reason=foreign-first-party-marker, above the strategy split, so the token reaches neither GoTrue nor the sub-claim lane. DEFAULT IS `shadow`, NOT `off`, and the divergence is deliberate \u2014 the shadow lines have been live in prod since 68863974b, and they are the soak instrument. Defaulting to `off` would silently switch off the measurement this flag exists to act on: the channel-quarantine-mode landmine (ENG-6252) in its exact shape. A flag-read failure degrades to `shadow` for the same reason it fails open elsewhere on this path: this is the platform front door, and a `feature_flags` blip must not start refusing the fleet. Deliberately NO envVar. The consumer is the API Lambda, whose environment block is at the AWS 4KB cap (ENG-7972), and an envVar is highest-precedence per ADR-0022 \u2014 a literal in sst.config.ts would pin every stage and make the admin control do nothing (ENG-8303). The flag value IS the kill switch: a flip back to shadow takes effect within the ~60s evaluation cache, with no deploy.",
@@ -34106,7 +34106,15 @@ var INTEGRATION_REGISTRY = [
34106
34106
  // integration_rate_cards; this only declares the model + meter.
34107
34107
  premium: {
34108
34108
  pricing: "usage",
34109
- note: "Billed per minute of real-time voice conversation.",
34109
+ // THE OPT-IN AUTHORISES BOTH METERS, SO THE NOTE NAMES BOTH (ENG-9913).
34110
+ // This string is the point of sale — it renders in the add-integration
34111
+ // dialog an org admin reads before opting in — and it described only
34112
+ // `voice_session` while the same opt-in, and the same $50 pool, had also
34113
+ // covered Ninja Notes cloud transcription since ENG-9870. "Of audio"
34114
+ // rather than "of meeting" is load-bearing: the two capture sources post
34115
+ // independently, so an hour-long meeting on default settings spends two
34116
+ // audio-hours (ADR-0063 §2a).
34117
+ note: "Billed per minute of real-time voice conversation, and per hour of audio for Ninja Notes cloud transcription \u2014 both against the same monthly budget.",
34110
34118
  meters: [
34111
34119
  { event_type: "voice_session", unit: "minute" },
34112
34120
  // ENG-9870. Ninja Notes cloud transcription, on the SAME definition id
@@ -34118,16 +34126,24 @@ var INTEGRATION_REGISTRY = [
34118
34126
  // `audio_hour`, not `minute`. The vendor prices per hour and the sell
34119
34127
  // price is per hour, so hours store exactly (50 minor) where minutes
34120
34128
  // would be 0.8333 repeating; `integration_usage_events.units` is
34121
- // `numeric`, so a 30-minute meeting records 0.5 units precisely. This
34129
+ // `numeric`, so 30 minutes of audio records 0.5 units precisely. This
34122
34130
  // is the answer to ENG-9773 §10.1's open question about whether hours
34123
34131
  // convert to minutes or the vocabulary gains hours.
34124
34132
  //
34125
- // NOTHING RECORDS THIS EVENT YET. Metering belongs at the chokepoint
34126
- // where audio is actually sent, and ENG-9868 left the transport
34127
- // injected with no implementation. The rate card exists first
34128
- // deliberately: a billable event with no USD rate makes
34129
- // `evaluateUsageBudget` throw, and on a SHARED pool that would break
34130
- // Voice Chat's budget check, not just this one.
34133
+ // AN AUDIO-HOUR IS NOT A MEETING-HOUR (ENG-9913, Brad, 2026-09-03). The
34134
+ // Mac captures system audio and the microphone as two independent
34135
+ // pipelines that post separately, and both default on, so the ordinary
34136
+ // meeting records two units per wall-clock hour. That is deliberate: the
34137
+ // audio-hour is what we buy, since xAI bills on the duration uploaded.
34138
+ // Pricing per meeting-hour would have varied the markup with a capture
34139
+ // toggle and could only have been built on a client's assertion about
34140
+ // how many pipelines were live. See ADR-0063 §2a.
34141
+ //
34142
+ // THE CHOKEPOINT EXISTS NOW — `POST /notes/transcribe` (ENG-9913)
34143
+ // reserves against this meter before the vendor call, from the bytes it
34144
+ // received. The rate card still had to land first, in ENG-9870: a
34145
+ // billable event with no USD rate makes `evaluateUsageBudget` throw, and
34146
+ // on a SHARED pool that would break Voice Chat's budget check too.
34131
34147
  { event_type: "transcription", unit: "audio_hour" }
34132
34148
  ]
34133
34149
  }
@@ -35024,7 +35040,18 @@ var INTEGRATION_REGISTRY = [
35024
35040
  // premium surface — is the structurally right answer and the follow-up.
35025
35041
  premium: {
35026
35042
  pricing: "included",
35027
- note: "Notes are included in your plan. Transcription runs on your own Mac and no platform summariser runs, so nothing is metered per note \u2014 the agent turn that reads it is billed as agent-hours like any other. Live meeting advising is the one exception: it is turned on separately, and billed per minute the advisor is armed."
35043
+ // "THE ONE EXCEPTION" BECAME TWO (ENG-9913). Cloud transcription is the
35044
+ // second metered capability under this integration, and this sentence was
35045
+ // true only for as long as its flag stayed off — it would have become
35046
+ // false on arming, silently, in a consent dialog. Both exceptions are
35047
+ // written the same way, and both are still off until someone turns them
35048
+ // on, so the sentence is accurate before and after the rollout.
35049
+ //
35050
+ // "Per hour of AUDIO" is deliberate here too: the two capture sources post
35051
+ // independently, so an hour-long meeting on default settings spends two
35052
+ // audio-hours (ADR-0063 §2a). The Mac's own caption carries the fuller
35053
+ // explanation, at the point where the person choosing it can read it.
35054
+ note: "Notes are included in your plan. Transcription runs on your own Mac by default and no platform summariser runs, so nothing is metered per note \u2014 the agent turn that reads it is billed as agent-hours like any other. Two capabilities are turned on separately and billed when they are: live meeting advising, per minute the advisor is armed; and cloud transcription, per hour of audio."
35028
35055
  }
35029
35056
  },
35030
35057
  {
@@ -37918,6 +37945,18 @@ var FLAG_REGISTRY = [
37918
37945
  // agent-hours-billing-enabled.
37919
37946
  sensitive: true
37920
37947
  },
37948
+ {
37949
+ key: "cloud-speech-to-text",
37950
+ description: 'Cloud transcription in Ninja Notes (ENG-9913). When ON for an org, a person recording a meeting on the Mac may choose to have each capture window transcribed by xAI instead of on-device: the window is POSTed to /notes/transcribe, proxied to xAI with the platform key, metered as grok-voice/transcription per audio_hour, and the text comes back with no speaker labels. THIS IS OUR ROLLOUT GATE AND IT IS THE THIRD KEY, NOT THE FIRST. Three things must all be true before a window leaves the Mac: the org holds the grok-voice premium opt-in (authorisation, resolveCloudSttEntitlement), the user has elected it per-install (consent, CaptureSettings.cloudTranscriptionEnabled), and this flag is on (our own readiness). Without this flag the first two alone were sufficient, which is why it exists: resolveCloudSttEntitlement reports enabled for every org that ever bought Voice Chat, so the route would go live for all of them the moment it deployed. It is what finally lets the server emit `not_available` \u2014 CloudTranscriptionRefusal.notRolledOut has shipped since ENG-9869 carrying the sentence "Cloud transcription is not available for your organisation yet" with nothing able to produce it. Boolean gate; ships dark. OFF NEVER COSTS THE RECORDING. An org without the flag \u2014 or a flag read that fails \u2014 transcribes on-device exactly as before; the microphone still starts and the note is still transcribed and delivered. Same direction as live-meeting-advisor and for the same reason: the user pressed Record and a recording is what they are owed. It is also what makes rollback cheap, because flipping this off mid-flight degrades in-progress meetings to on-device rather than breaking them. Deliberately NO envVar. This is an org-scoped gate, and an envVar is highest-precedence per ADR-0022 \u2014 a literal in sst.config.ts would pin every stage and make the admin control do nothing, the ENG-8303 class exactly. No `since`: the reader is the API route, not a host-side agt-cli consumer, so this does not depend on fleet convergence. The Mac client is a separate fleet that reports no version at all, which is precisely why the gate has to live server-side.',
37951
+ flagType: "boolean",
37952
+ defaultValue: false,
37953
+ // Turning it ON starts sending meeting audio — INCLUDING the other side of
37954
+ // the call, whose participants have not been asked — to a third-party
37955
+ // vendor, and arms a per-audio-hour meter against the org's shared
37956
+ // grok-voice budget. Either half alone would warrant the confirm ADR-0022
37957
+ // §4 reserves for deliberate, audited flips.
37958
+ sensitive: true
37959
+ },
37921
37960
  {
37922
37961
  key: "first-party-token-refusal",
37923
37962
  description: "ENG-8972 AC4 / ADR-0076 decision 2 \u2014 whether `resolveUserId` (packages/api/src/middleware/auth.ts) REFUSES a bearer token carrying a first-party marker that is foreign to the host-exchange lane. SUPABASE_JWT_SECRET is symmetric and signs twelve distinct token families; the generic Bearer door has always admitted any of them that mints a uuid-shaped `sub`, so a token minted for one purpose authenticates as a user for every purpose. off = the capture never runs (no log, no refusal). shadow = record the population an enforcement flip would change, on BOTH admitting lanes (lane=gotrue, lane=sub-claim), and refuse nothing \u2014 this is exactly the behaviour compiled into prod today. enforce = return null with reason=foreign-first-party-marker, above the strategy split, so the token reaches neither GoTrue nor the sub-claim lane. DEFAULT IS `shadow`, NOT `off`, and the divergence is deliberate \u2014 the shadow lines have been live in prod since 68863974b, and they are the soak instrument. Defaulting to `off` would silently switch off the measurement this flag exists to act on: the channel-quarantine-mode landmine (ENG-6252) in its exact shape. A flag-read failure degrades to `shadow` for the same reason it fails open elsewhere on this path: this is the platform front door, and a `feature_flags` blip must not start refusing the fleet. Deliberately NO envVar. The consumer is the API Lambda, whose environment block is at the AWS 4KB cap (ENG-7972), and an envVar is highest-precedence per ADR-0022 \u2014 a literal in sst.config.ts would pin every stage and make the admin control do nothing (ENG-8303). The flag value IS the kill switch: a flip back to shadow takes effect within the ~60s evaluation cache, with no deploy.",
@@ -34264,7 +34264,15 @@ var INTEGRATION_REGISTRY = [
34264
34264
  // integration_rate_cards; this only declares the model + meter.
34265
34265
  premium: {
34266
34266
  pricing: "usage",
34267
- note: "Billed per minute of real-time voice conversation.",
34267
+ // THE OPT-IN AUTHORISES BOTH METERS, SO THE NOTE NAMES BOTH (ENG-9913).
34268
+ // This string is the point of sale — it renders in the add-integration
34269
+ // dialog an org admin reads before opting in — and it described only
34270
+ // `voice_session` while the same opt-in, and the same $50 pool, had also
34271
+ // covered Ninja Notes cloud transcription since ENG-9870. "Of audio"
34272
+ // rather than "of meeting" is load-bearing: the two capture sources post
34273
+ // independently, so an hour-long meeting on default settings spends two
34274
+ // audio-hours (ADR-0063 §2a).
34275
+ note: "Billed per minute of real-time voice conversation, and per hour of audio for Ninja Notes cloud transcription \u2014 both against the same monthly budget.",
34268
34276
  meters: [
34269
34277
  { event_type: "voice_session", unit: "minute" },
34270
34278
  // ENG-9870. Ninja Notes cloud transcription, on the SAME definition id
@@ -34276,16 +34284,24 @@ var INTEGRATION_REGISTRY = [
34276
34284
  // `audio_hour`, not `minute`. The vendor prices per hour and the sell
34277
34285
  // price is per hour, so hours store exactly (50 minor) where minutes
34278
34286
  // would be 0.8333 repeating; `integration_usage_events.units` is
34279
- // `numeric`, so a 30-minute meeting records 0.5 units precisely. This
34287
+ // `numeric`, so 30 minutes of audio records 0.5 units precisely. This
34280
34288
  // is the answer to ENG-9773 §10.1's open question about whether hours
34281
34289
  // convert to minutes or the vocabulary gains hours.
34282
34290
  //
34283
- // NOTHING RECORDS THIS EVENT YET. Metering belongs at the chokepoint
34284
- // where audio is actually sent, and ENG-9868 left the transport
34285
- // injected with no implementation. The rate card exists first
34286
- // deliberately: a billable event with no USD rate makes
34287
- // `evaluateUsageBudget` throw, and on a SHARED pool that would break
34288
- // Voice Chat's budget check, not just this one.
34291
+ // AN AUDIO-HOUR IS NOT A MEETING-HOUR (ENG-9913, Brad, 2026-09-03). The
34292
+ // Mac captures system audio and the microphone as two independent
34293
+ // pipelines that post separately, and both default on, so the ordinary
34294
+ // meeting records two units per wall-clock hour. That is deliberate: the
34295
+ // audio-hour is what we buy, since xAI bills on the duration uploaded.
34296
+ // Pricing per meeting-hour would have varied the markup with a capture
34297
+ // toggle and could only have been built on a client's assertion about
34298
+ // how many pipelines were live. See ADR-0063 §2a.
34299
+ //
34300
+ // THE CHOKEPOINT EXISTS NOW — `POST /notes/transcribe` (ENG-9913)
34301
+ // reserves against this meter before the vendor call, from the bytes it
34302
+ // received. The rate card still had to land first, in ENG-9870: a
34303
+ // billable event with no USD rate makes `evaluateUsageBudget` throw, and
34304
+ // on a SHARED pool that would break Voice Chat's budget check too.
34289
34305
  { event_type: "transcription", unit: "audio_hour" }
34290
34306
  ]
34291
34307
  }
@@ -35182,7 +35198,18 @@ var INTEGRATION_REGISTRY = [
35182
35198
  // premium surface — is the structurally right answer and the follow-up.
35183
35199
  premium: {
35184
35200
  pricing: "included",
35185
- note: "Notes are included in your plan. Transcription runs on your own Mac and no platform summariser runs, so nothing is metered per note \u2014 the agent turn that reads it is billed as agent-hours like any other. Live meeting advising is the one exception: it is turned on separately, and billed per minute the advisor is armed."
35201
+ // "THE ONE EXCEPTION" BECAME TWO (ENG-9913). Cloud transcription is the
35202
+ // second metered capability under this integration, and this sentence was
35203
+ // true only for as long as its flag stayed off — it would have become
35204
+ // false on arming, silently, in a consent dialog. Both exceptions are
35205
+ // written the same way, and both are still off until someone turns them
35206
+ // on, so the sentence is accurate before and after the rollout.
35207
+ //
35208
+ // "Per hour of AUDIO" is deliberate here too: the two capture sources post
35209
+ // independently, so an hour-long meeting on default settings spends two
35210
+ // audio-hours (ADR-0063 §2a). The Mac's own caption carries the fuller
35211
+ // explanation, at the point where the person choosing it can read it.
35212
+ note: "Notes are included in your plan. Transcription runs on your own Mac by default and no platform summariser runs, so nothing is metered per note \u2014 the agent turn that reads it is billed as agent-hours like any other. Two capabilities are turned on separately and billed when they are: live meeting advising, per minute the advisor is armed; and cloud transcription, per hour of audio."
35186
35213
  }
35187
35214
  },
35188
35215
  {
@@ -38076,6 +38103,18 @@ var FLAG_REGISTRY = [
38076
38103
  // agent-hours-billing-enabled.
38077
38104
  sensitive: true
38078
38105
  },
38106
+ {
38107
+ key: "cloud-speech-to-text",
38108
+ description: 'Cloud transcription in Ninja Notes (ENG-9913). When ON for an org, a person recording a meeting on the Mac may choose to have each capture window transcribed by xAI instead of on-device: the window is POSTed to /notes/transcribe, proxied to xAI with the platform key, metered as grok-voice/transcription per audio_hour, and the text comes back with no speaker labels. THIS IS OUR ROLLOUT GATE AND IT IS THE THIRD KEY, NOT THE FIRST. Three things must all be true before a window leaves the Mac: the org holds the grok-voice premium opt-in (authorisation, resolveCloudSttEntitlement), the user has elected it per-install (consent, CaptureSettings.cloudTranscriptionEnabled), and this flag is on (our own readiness). Without this flag the first two alone were sufficient, which is why it exists: resolveCloudSttEntitlement reports enabled for every org that ever bought Voice Chat, so the route would go live for all of them the moment it deployed. It is what finally lets the server emit `not_available` \u2014 CloudTranscriptionRefusal.notRolledOut has shipped since ENG-9869 carrying the sentence "Cloud transcription is not available for your organisation yet" with nothing able to produce it. Boolean gate; ships dark. OFF NEVER COSTS THE RECORDING. An org without the flag \u2014 or a flag read that fails \u2014 transcribes on-device exactly as before; the microphone still starts and the note is still transcribed and delivered. Same direction as live-meeting-advisor and for the same reason: the user pressed Record and a recording is what they are owed. It is also what makes rollback cheap, because flipping this off mid-flight degrades in-progress meetings to on-device rather than breaking them. Deliberately NO envVar. This is an org-scoped gate, and an envVar is highest-precedence per ADR-0022 \u2014 a literal in sst.config.ts would pin every stage and make the admin control do nothing, the ENG-8303 class exactly. No `since`: the reader is the API route, not a host-side agt-cli consumer, so this does not depend on fleet convergence. The Mac client is a separate fleet that reports no version at all, which is precisely why the gate has to live server-side.',
38109
+ flagType: "boolean",
38110
+ defaultValue: false,
38111
+ // Turning it ON starts sending meeting audio — INCLUDING the other side of
38112
+ // the call, whose participants have not been asked — to a third-party
38113
+ // vendor, and arms a per-audio-hour meter against the org's shared
38114
+ // grok-voice budget. Either half alone would warrant the confirm ADR-0022
38115
+ // §4 reserves for deliberate, audited flips.
38116
+ sensitive: true
38117
+ },
38079
38118
  {
38080
38119
  key: "first-party-token-refusal",
38081
38120
  description: "ENG-8972 AC4 / ADR-0076 decision 2 \u2014 whether `resolveUserId` (packages/api/src/middleware/auth.ts) REFUSES a bearer token carrying a first-party marker that is foreign to the host-exchange lane. SUPABASE_JWT_SECRET is symmetric and signs twelve distinct token families; the generic Bearer door has always admitted any of them that mints a uuid-shaped `sub`, so a token minted for one purpose authenticates as a user for every purpose. off = the capture never runs (no log, no refusal). shadow = record the population an enforcement flip would change, on BOTH admitting lanes (lane=gotrue, lane=sub-claim), and refuse nothing \u2014 this is exactly the behaviour compiled into prod today. enforce = return null with reason=foreign-first-party-marker, above the strategy split, so the token reaches neither GoTrue nor the sub-claim lane. DEFAULT IS `shadow`, NOT `off`, and the divergence is deliberate \u2014 the shadow lines have been live in prod since 68863974b, and they are the soak instrument. Defaulting to `off` would silently switch off the measurement this flag exists to act on: the channel-quarantine-mode landmine (ENG-6252) in its exact shape. A flag-read failure degrades to `shadow` for the same reason it fails open elsewhere on this path: this is the platform front door, and a `feature_flags` blip must not start refusing the fleet. Deliberately NO envVar. The consumer is the API Lambda, whose environment block is at the AWS 4KB cap (ENG-7972), and an envVar is highest-precedence per ADR-0022 \u2014 a literal in sst.config.ts would pin every stage and make the admin control do nothing (ENG-8303). The flag value IS the kill switch: a flip back to shadow takes effect within the ~60s evaluation cache, with no deploy.",