@rosthq/cli 0.7.110 → 0.7.111

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/dist/index.js CHANGED
@@ -50253,7 +50253,7 @@ Agents can suggest commitments and report progress. They should not create a new
50253
50253
  order: 61,
50254
50254
  title: "Signal guide",
50255
50255
  summary: "How to define and read measurables so the company runs on evidence instead of status theater.",
50256
- version: "2026-07-20.1",
50256
+ version: "2026-07-20.2",
50257
50257
  public: true,
50258
50258
  audiences: ["human", "cli", "mcp", "in_app_agent"],
50259
50259
  stages: ["operating_rhythm"],
@@ -50312,7 +50312,7 @@ Avoid vanity numbers, manual-only status fields, and metrics nobody can act on.
50312
50312
  - Add a measurable: \`measurable.create\` (scope: seat) defines a measurable a seat owns \u2014 name, unit, direction, target, cadence. The seat owns it; readings attach to it afterward.
50313
50313
  - Record a reading: \`{{cli}} status record --measurable-id <id> --value <n>\` (\`status.record\`, scope: seat) writes a status event with the reading. This is not gated. An agent's \`status.record\` never downgrades a human-confirmed reading: a routine agent read that lands on a period a human already confirmed leaves the confirmed value and its confirmation intact (invariants #7/#8 \u2014 agents recommend; humans decide).
50314
50314
  - Confirm a reading: \`{{cli}} signal confirm\` / \`signal.confirm_reading\` / \`rost_confirm_signal_reading\` marks a reading human-verified. \`signal.correct_reading\` / \`rost_correct_signal_reading\` overwrites a reading with a human-confirmed value.
50315
- - Draft first readings from a connected source: \`signal.draft_first_readings\` pulls a measurable's bound source once (read-only, SSRF-guarded \u2014 the same pull path as \`signal.preview\`) and lands the value as a DRAFT reading for the current period, for a human to confirm. It is for a measurable that has a connected source but no confirmed reading yet: the Signal page shows a "Draft first readings" button on those rows so the row stops being a permanent blank. Nothing is published \u2014 a human clears the draft with \`signal.confirm_reading\` (invariant #7) \u2014 and it never fabricates a value: a non-numeric or blocked pull drafts nothing and says so.
50315
+ - Draft first readings from a connected source: \`signal.draft_first_readings\` pulls a measurable's bound source once (read-only, SSRF-guarded \u2014 the same pull path as \`signal.preview\`) and lands the value as a DRAFT reading for the current period, for a human to confirm. It is strictly for a measurable that has a connected source but has never had a confirmed reading \u2014 in ANY period, not just the current one. A measurable that already carries a confirmed reading (even an older one) is rejected with an "already has readings" outcome and no pull happens, so the first-reading flow can never be repurposed as an ongoing draft source; use \`signal.report\` to add a current-period datapoint or \`signal.correct_reading\` to change a confirmed value instead. The Signal page shows a "Draft first readings" button on the eligible (never-measured) rows so the row stops being a permanent blank. Nothing is published \u2014 a human clears the draft with \`signal.confirm_reading\` (invariant #7) \u2014 and it never fabricates a value: a non-numeric or blocked pull drafts nothing and says so.
50316
50316
 
50317
50317
  ## Run Signal without an agent
50318
50318
 
@@ -57737,6 +57737,7 @@ ${usage()}
57737
57737
  let failed = false;
57738
57738
  let activeSessions = 0;
57739
57739
  const activeTurnExecutionIds = /* @__PURE__ */ new Set();
57740
+ let serverHeartbeatContractVersion = null;
57740
57741
  do {
57741
57742
  const detectedCapabilities = await detectCapabilities(cliVersion, env, homeDir, config2.maxSessionsOverride);
57742
57743
  const capabilities = capabilitiesForConfiguredRuntime(detectedCapabilities, config2.runtime);
@@ -57745,7 +57746,8 @@ ${usage()}
57745
57746
  const heartbeat = await post2(fetchImpl, options.appUrl, "/api/runner/heartbeat", buildHeartbeatBody({
57746
57747
  capabilities,
57747
57748
  telemetry: detectTelemetry(capabilities, env, activeSessions, cliVersion),
57748
- activeTurnExecutionIds: [...activeTurnExecutionIds]
57749
+ activeTurnExecutionIds: [...activeTurnExecutionIds],
57750
+ serverContractVersion: serverHeartbeatContractVersion
57749
57751
  }), state.runner_secret);
57750
57752
  if (heartbeat.status !== 200) {
57751
57753
  options.io.stderr.write(`heartbeat ${heartbeat.status}: ${redactForLog(JSON.stringify(heartbeat.json))}
@@ -57753,6 +57755,10 @@ ${usage()}
57753
57755
  failed = true;
57754
57756
  } else {
57755
57757
  heartbeatSucceeded = true;
57758
+ const learnedVersion = readHeartbeatContractVersion(heartbeat.json);
57759
+ if (learnedVersion !== null) {
57760
+ serverHeartbeatContractVersion = learnedVersion;
57761
+ }
57756
57762
  options.io.stdout.write(`heartbeat ok #${beat} runner_id=${state.runner_id} claude=${capabilities.claude.installed} codex=${capabilities.codex.installed}
57757
57763
  `);
57758
57764
  }
@@ -57766,7 +57772,11 @@ ${usage()}
57766
57772
  capabilities,
57767
57773
  io: options.io,
57768
57774
  telemetry: () => detectTelemetry(capabilities, env, activeSessions, cliVersion),
57769
- activeTurnIds: () => [...activeTurnExecutionIds]
57775
+ activeTurnIds: () => [...activeTurnExecutionIds],
57776
+ serverContractVersion: () => serverHeartbeatContractVersion,
57777
+ recordServerContractVersion: (v) => {
57778
+ serverHeartbeatContractVersion = v;
57779
+ }
57770
57780
  });
57771
57781
  try {
57772
57782
  await claimAndExecute(fetchImpl, options.appUrl, state, config2, options.io, localRuntime, activeTurnExecutionIds);
@@ -57791,13 +57801,17 @@ ${usage()}
57791
57801
  localRuntime,
57792
57802
  capabilities,
57793
57803
  () => detectTelemetry(capabilities, env, activeSessions, cliVersion),
57794
- activeTurnExecutionIds
57804
+ activeTurnExecutionIds,
57805
+ () => serverHeartbeatContractVersion,
57806
+ (v) => {
57807
+ serverHeartbeatContractVersion = v;
57808
+ }
57795
57809
  );
57796
57810
  }
57797
57811
  } while (!config2.once);
57798
57812
  return heartbeatSucceeded && !failed ? 0 : 1;
57799
57813
  }
57800
- async function waitForNextHeartbeat(fetchImpl, appUrl2, state, config2, io, runtime, capabilities, telemetry, activeTurnExecutionIds) {
57814
+ async function waitForNextHeartbeat(fetchImpl, appUrl2, state, config2, io, runtime, capabilities, telemetry, activeTurnExecutionIds, serverContractVersion, recordServerContractVersion) {
57801
57815
  const deadline = Date.now() + config2.heartbeatMs;
57802
57816
  const stopWait = runtime ? startTurnHeartbeat({
57803
57817
  fetchImpl,
@@ -57807,7 +57821,9 @@ async function waitForNextHeartbeat(fetchImpl, appUrl2, state, config2, io, runt
57807
57821
  capabilities,
57808
57822
  io,
57809
57823
  telemetry,
57810
- activeTurnIds: () => [...activeTurnExecutionIds]
57824
+ activeTurnIds: () => [...activeTurnExecutionIds],
57825
+ serverContractVersion,
57826
+ recordServerContractVersion
57811
57827
  }) : null;
57812
57828
  try {
57813
57829
  while (Date.now() < deadline) {
@@ -57827,12 +57843,17 @@ async function waitForNextHeartbeat(fetchImpl, appUrl2, state, config2, io, runt
57827
57843
  stopWait?.();
57828
57844
  }
57829
57845
  }
57846
+ var HEARTBEAT_ACTIVE_TURN_MIN_CONTRACT_VERSION = 2;
57847
+ function readHeartbeatContractVersion(json2) {
57848
+ const value = json2.heartbeat_contract_version;
57849
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
57850
+ }
57830
57851
  function buildHeartbeatBody(input) {
57831
57852
  const body = {
57832
57853
  capabilities: input.capabilities,
57833
57854
  telemetry: input.telemetry
57834
57855
  };
57835
- if (input.activeTurnExecutionIds.length > 0) {
57856
+ if (input.activeTurnExecutionIds.length > 0 && (input.serverContractVersion ?? 0) >= HEARTBEAT_ACTIVE_TURN_MIN_CONTRACT_VERSION) {
57836
57857
  body.active_turn_execution_ids = input.activeTurnExecutionIds;
57837
57858
  }
57838
57859
  return body;
@@ -57853,11 +57874,17 @@ function startTurnHeartbeat(deps) {
57853
57874
  const response = await post2(deps.fetchImpl, deps.appUrl, "/api/runner/heartbeat", buildHeartbeatBody({
57854
57875
  capabilities: deps.capabilities,
57855
57876
  telemetry: deps.telemetry(),
57856
- activeTurnExecutionIds: deps.activeTurnIds()
57877
+ activeTurnExecutionIds: deps.activeTurnIds(),
57878
+ serverContractVersion: deps.serverContractVersion()
57857
57879
  }), deps.secret, controller.signal);
57858
57880
  if (!stopped && response.status !== 200) {
57859
57881
  deps.io.stderr.write(`turn heartbeat ${response.status}: ${redactForLog(JSON.stringify(response.json))}
57860
57882
  `);
57883
+ } else if (!stopped && response.status === 200) {
57884
+ const learnedVersion = readHeartbeatContractVersion(response.json);
57885
+ if (learnedVersion !== null) {
57886
+ deps.recordServerContractVersion(learnedVersion);
57887
+ }
57861
57888
  }
57862
57889
  } catch (error51) {
57863
57890
  if (!stopped) {