@rosthq/cli 0.7.109 → 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.
@@ -1 +1 @@
1
- {"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../src/generator/groups.ts"],"names":[],"mappings":"AAuCA,OAAO,EAAgB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAM1D,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAKhF,CAAC;AAitCF,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAmPjD,CAAC"}
1
+ {"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../src/generator/groups.ts"],"names":[],"mappings":"AAuCA,OAAO,EAAgB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAM1D,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAKhF,CAAC;AAotCF,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAmPjD,CAAC"}
package/dist/index.js CHANGED
@@ -41285,6 +41285,8 @@ var signalSummarySchema = external_exports.object({
41285
41285
  target: external_exports.number(),
41286
41286
  cadence: external_exports.enum(["daily", "weekly", "monthly", "quarterly", "annual"]),
41287
41287
  source: external_exports.enum(["agent", "human", "integration"]),
41288
+ // DER-2066: read-time truth of how the measurable is currently fed (DER-1904 effectiveSource) — an active binding reads "auto" even while declared source stays "human". Additive; does not replace source.
41289
+ effective_source: external_exports.enum(["auto", "agent", "human"]),
41288
41290
  latest_reading_id: uuidSchema9.nullable(),
41289
41291
  latest_value: external_exports.number().nullable(),
41290
41292
  latest_period_start: external_exports.string().nullable(),
@@ -43433,6 +43435,10 @@ var fleetOverviewRowSchema = external_exports.object({
43433
43435
  seat_id: uuidSchema14,
43434
43436
  seat_name: external_exports.string(),
43435
43437
  seat_path: external_exports.string(),
43438
+ // DER-2068 (N-2): the seat's immediate parent seat name (null for the root
43439
+ // seat, which has none) — the web fleet row renders "Reports to
43440
+ // {parent_seat_name}" instead of the internal ltree path as visible text.
43441
+ parent_seat_name: external_exports.string().nullable(),
43436
43442
  lane: agentLaneSchema2.nullable(),
43437
43443
  agent_status: external_exports.enum(["draft", "dry_run", "live", "paused", "decommissioned", "vacant"]),
43438
43444
  live: external_exports.boolean(),
@@ -43452,7 +43458,12 @@ var fleetOverviewRowSchema = external_exports.object({
43452
43458
  // runner-lane host-resource meter; null for cloud/mcp_session lanes.
43453
43459
  host_resource: fleetHostResourceSchema.nullable(),
43454
43460
  work_progress: fleetWorkProgressSchema,
43455
- work_progress_conflict: external_exports.boolean()
43461
+ work_progress_conflict: external_exports.boolean(),
43462
+ // DER-2066: true for Forge system seats (forge.developer_team.*) so a consumer can
43463
+ // group/collapse them the way the web /agents fleet page does (DER-1919). Kept on the
43464
+ // SHARED row schema (not a list_fleet-only extension) so agent.list_fleet and
43465
+ // agent.fleet_digest stay in lockstep — the DER-1895 reference-equality guard. Additive.
43466
+ is_system: external_exports.boolean()
43456
43467
  }).strict();
43457
43468
  var agentListFleetOutputSchema = external_exports.object({
43458
43469
  agents: external_exports.array(fleetOverviewRowSchema)
@@ -46362,19 +46373,82 @@ var softwareVercelDeployPreviewOutputSchema = external_exports.object({
46362
46373
  status: softwareVercelDeploymentStatusSchema
46363
46374
  });
46364
46375
 
46365
- // ../../packages/protocol/src/software-factory-events.ts
46376
+ // ../../packages/protocol/src/software-factory-verification.ts
46366
46377
  var uuid13 = external_exports.string().uuid();
46378
+ var DELIVERY_SURFACE_STATUS_VALUES = [
46379
+ "merged",
46380
+ "deployed",
46381
+ "verification_running",
46382
+ "verified_live",
46383
+ "failed",
46384
+ "inconclusive",
46385
+ "waived"
46386
+ ];
46387
+ var deliverySurfaceStatusSchema = external_exports.enum(DELIVERY_SURFACE_STATUS_VALUES);
46388
+ var DELIVERY_SURFACE_KIND_VALUES = ["page", "api", "route_health"];
46389
+ var deliverySurfaceKindSchema = external_exports.enum(DELIVERY_SURFACE_KIND_VALUES);
46390
+ var DELIVERY_VERIFICATION_STATUS_VALUES = [
46391
+ "pending",
46392
+ "awaiting_deployment",
46393
+ "running",
46394
+ "verified",
46395
+ "failed",
46396
+ "inconclusive",
46397
+ "waived"
46398
+ ];
46399
+ var deliveryVerificationStatusSchema = external_exports.enum(DELIVERY_VERIFICATION_STATUS_VALUES);
46400
+ var affectedSurfaceSchema = external_exports.object({
46401
+ kind: deliverySurfaceKindSchema,
46402
+ path: external_exports.string().min(1).max(512)
46403
+ }).strict();
46404
+ var deliverySurfaceEvidenceSchema = external_exports.object({
46405
+ statusCode: external_exports.number().int().nullable(),
46406
+ durationMs: external_exports.number().nonnegative().nullable(),
46407
+ summary: external_exports.string().max(500)
46408
+ }).strict().superRefine((value, ctx) => {
46409
+ if (hasSecretShapedValue(value.summary)) {
46410
+ ctx.addIssue({
46411
+ code: external_exports.ZodIssueCode.custom,
46412
+ path: ["summary"],
46413
+ message: "evidence summary must not contain raw secret material"
46414
+ });
46415
+ }
46416
+ });
46417
+ var deliverySurfaceReportSchema = external_exports.object({
46418
+ path: external_exports.string().min(1).max(512),
46419
+ kind: deliverySurfaceKindSchema,
46420
+ status: deliverySurfaceStatusSchema,
46421
+ sha: external_exports.string().nullable(),
46422
+ evidence: external_exports.string().max(500).nullable(),
46423
+ checkedAt: external_exports.string().nullable()
46424
+ }).strict();
46425
+ var deliveryVerificationReportSchema = external_exports.object({
46426
+ buildRequestId: uuid13,
46427
+ status: deliveryVerificationStatusSchema,
46428
+ expectedSha: external_exports.string().min(1),
46429
+ observedSha: external_exports.string().nullable(),
46430
+ testTenantRef: external_exports.string().min(1),
46431
+ startedAt: external_exports.string().nullable(),
46432
+ verifiedAt: external_exports.string().nullable(),
46433
+ kickbackWorkOrderId: uuid13.nullable(),
46434
+ waiverDecisionId: uuid13.nullable(),
46435
+ waiverReason: external_exports.string().nullable(),
46436
+ surfaces: external_exports.array(deliverySurfaceReportSchema)
46437
+ }).strict();
46438
+
46439
+ // ../../packages/protocol/src/software-factory-events.ts
46440
+ var uuid14 = external_exports.string().uuid();
46367
46441
  var softwareFactoryPhaseRunCompletedEventSchema = external_exports.object({
46368
- tenantId: uuid13,
46369
- phaseRunId: uuid13,
46370
- buildRequestId: uuid13.nullish(),
46442
+ tenantId: uuid14,
46443
+ phaseRunId: uuid14,
46444
+ buildRequestId: uuid14.nullish(),
46371
46445
  status: external_exports.enum(["passed", "failed"]),
46372
46446
  idempotencyKey: external_exports.string().min(1).optional()
46373
46447
  }).strict();
46374
46448
  var softwareFactoryBudgetExhaustedEventSchema = external_exports.object({
46375
- tenantId: uuid13,
46376
- phaseRunId: uuid13,
46377
- buildRequestId: uuid13,
46449
+ tenantId: uuid14,
46450
+ phaseRunId: uuid14,
46451
+ buildRequestId: uuid14,
46378
46452
  reason: external_exports.string().min(1),
46379
46453
  idempotencyKey: external_exports.string().min(1).optional()
46380
46454
  }).strict();
@@ -47765,7 +47839,7 @@ The Compass is drafted, then activated by a human through supersession.
47765
47839
  order: 15,
47766
47840
  title: "AICOS chat guide",
47767
47841
  summary: "How the AI Chief of Staff chat works in the authenticated app shell and what it can safely do today.",
47768
- version: "2026-07-15.1",
47842
+ version: "2026-07-20.1",
47769
47843
  public: true,
47770
47844
  audiences: ["human", "in_app_agent"],
47771
47845
  stages: ["company_setup", "operating_rhythm"],
@@ -47783,7 +47857,10 @@ The Compass is drafted, then activated by a human through supersession.
47783
47857
  "agent.configure_tools",
47784
47858
  "agent.run_dry_run",
47785
47859
  "agent.go_live",
47786
- "charter.sign_manifest"
47860
+ "charter.sign_manifest",
47861
+ "tool_grants.agent.update",
47862
+ "agent.update_schedule",
47863
+ "skill.assign_to_seat"
47787
47864
  ],
47788
47865
  legal: {
47789
47866
  publicRisk: "low",
@@ -47879,6 +47956,8 @@ Durable agent lifecycle changes still use the existing command surface:
47879
47956
  - \`agent.go_live\` only happens after the required approval gate.
47880
47957
  - \`confirmation.approve\` is the human approval command for pending confirmations.
47881
47958
 
47959
+ When a user asks AICOS to change an agent's capabilities or configuration \u2014 grant a tool to a seat, set or clear an agent's schedule, or assign a skill \u2014 AICOS acts on the request instead of only explaining it. It stages the change as a draft-and-confirm proposal through the governed command and links the pending confirmation on the Approvals queue for a human to approve. Each of these proposals (\`tool_grants.agent.update\`, \`agent.update_schedule\`, \`skill.assign_to_seat\`) is human-required and always-human, so nothing durable changes until a person confirms it and AICOS never applies a capability change on its own. When AICOS does not hold the command for a request, it names the exact gap and points to the seat page where a human can make the change, rather than returning a generic how-to answer.
47960
+
47882
47961
  If the user asks for report-only or no-action help, AICOS stays in plan-only mode and does not create or advance a lifecycle change. Seat-scoped staging only uses the server-authorized pinned seat from the session; client-supplied tenant or seat context is ignored as authority. Runner and MCP lanes do not bypass the same server command guards, and they do not grant hidden browser tool execution. Raw secrets, vault material, and secret-shaped metadata stay out of transcript storage.
47883
47962
 
47884
47963
  ## Seat-scoped AICOS
@@ -48488,7 +48567,7 @@ Decisions should be recorded as human decisions. Handoffs should attach to seats
48488
48567
  order: 45,
48489
48568
  title: "How agents work",
48490
48569
  summary: "How {{brand}} agents operate inside seats, use Charters, report work, and escalate beyond authority.",
48491
- version: "2026-07-18.1",
48570
+ version: "2026-07-20.1",
48492
48571
  public: true,
48493
48572
  audiences: ["human", "cli", "mcp", "in_app_agent"],
48494
48573
  stages: ["staffing", "operating_rhythm"],
@@ -48588,7 +48667,7 @@ Beyond each seat's own manifest, the company can set one ceiling on how much an
48588
48667
 
48589
48668
  The Agents page shows fleet health first: live state, work evidence, failed runs, open held actions, cost, and the next operator action. Work evidence distinguishes fresh real runs, stale runs, no completed run yet, queued or running work orders, and recent failures. A failure link opens the failed run itself; queued or stale rows route the operator to the seat's agent operations instead of pretending there is a hidden run to inspect. The page also shows configuration completeness for each staffed agent seat: Steward chain, lane or runtime substrate, vaulted credentials, granted tools, model config, sandbox dry run, and go-live state, each with a remediation link when incomplete. A seat's Trust Card drills into the same facts: run history, one-run detail, tool-call outcomes, product-visible errors, transcript references, loaded Skill versions, and held confirmations or escalations. The same seat's Activity tab also shows a curated business-activity ledger (counter tiles plus a run-grouped \`work.record\` feed) and a sessions browser with the full persisted transcript and artifacts for the selected run \u2014 a layer above the Trust Card's forensic trace, not a replacement for it. The fleet page's own **Work** tab mirrors that ledger across every seat in one filterable, seat-labelled stream. Open confirmations stay visible until they are decided or expire, even when they are older than the default activity window. Approving or rejecting a held confirmation from the seat page is scoped to that seat; rejection can include a short reason. For a live agent, the seat page's agent operations panel can queue one immediate governed run through the same \`agent.run_now\` command path used by CLI and MCP; it does not expand authority or change the saved schedule. Seat pages render legacy Charter manifest scope text with customer-safe labels when old setup data used internal placeholder wording.
48590
48669
 
48591
- Review the first dry runs, fleet overview, tool-call audit rows, escalations, deliverables, and Signal impact. A seat's completed-work area combines explicit deliverables, work-log evidence, and successful run summaries into one operator-facing trail with safe links labelled as internal, Linear, GitHub, or external. The fleet view at \`/agents\` shows every staffed agent seat at a glance; the agent-native equivalents are \`{{cli}} command agent.list_fleet --json '{}'\` / \`rost_list_agent_fleet\` for the compact overview, and \`{{cli}} agent fleet-digest --json\` / \`agent.fleet_digest\` / \`rost_get_dogfood_fleet_health_digest\` for the daily dogfood evidence bundle. The digest returns live/idle state, 24h/7d turns, 7d/30d spend, recent failed runs, unresolved product errors, failed notifications, and a next action per seat.
48670
+ Review the first dry runs, fleet overview, tool-call audit rows, escalations, deliverables, and Signal impact. A seat's completed-work area combines explicit deliverables, work-log evidence, and successful run summaries into one operator-facing trail with safe links labelled as internal, Linear, GitHub, or external. The fleet view at \`/agents\` shows every staffed agent seat at a glance; the agent-native equivalents are \`{{cli}} command agent.list_fleet --json '{}'\` / \`rost_list_agent_fleet\` for the compact overview (each seat marked with \`is_system\` for Forge system seats under \`forge.developer_team.*\`, so a caller can group or collapse them the way the web fleet page does), and \`{{cli}} agent fleet-digest --json\` / \`agent.fleet_digest\` / \`rost_get_dogfood_fleet_health_digest\` for the daily dogfood evidence bundle. The digest returns live/idle state, 24h/7d turns, 7d/30d spend, recent failed runs, unresolved product errors, failed notifications, and a next action per seat.
48592
48671
 
48593
48672
  For an operational audit, start with the \`/health\` dashboard or \`{{cli}} system health --json\` / \`system.health\` / \`rost_system_health\` before manually chaining fleet, run, Signal, Friction, task, integration, runner, and Sync commands. The dashboard, CLI, and MCP tool share the same command-backed system-health model, with server-derived tenant, member, requested-seat, or seat-token scope. Owners see tenant health; members see occupied-seat subtrees; seat-scoped tokens see their own seat plus authorized downstream seats, and optional \`seat_id\` narrowing can only reduce that server-derived scope. Seat pages link to \`/health?seat_id=<seat>\` for scoped follow-up. Every health finding includes a remediation action with a human label/link plus command id, schema reference, CLI command, help text, and human-gated marker for agents. Open Friction without an action task links to the Friction detail and gives agents the \`task.create\` then \`friction.link_task\` next step; the final issue resolution still requires the human \`friction.resolve\` decision.
48594
48673
 
@@ -48753,7 +48832,7 @@ External connectors are being rolled out provider by provider, conservatively (r
48753
48832
  order: 48,
48754
48833
  title: "CLI and MCP installation guide",
48755
48834
  summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
48756
- version: "2026-07-19.2",
48835
+ version: "2026-07-20.1",
48757
48836
  public: true,
48758
48837
  audiences: ["human", "cli", "mcp", "in_app_agent"],
48759
48838
  stages: ["company_setup", "staffing"],
@@ -49365,7 +49444,7 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
49365
49444
  | \`rost_get_current_compass\` | \`compass.get_current\` | Read the active and draft Compass versions and source documents. | Tenant | Call with \`{}\`. |
49366
49445
  | \`rost_list_compass_gaps\` | \`compass.list_gaps\` | List unanswered and answered Compass context gaps. | Tenant | Call with \`{}\` before answering gaps. |
49367
49446
  | \`rost_get_agent_status\` | \`agent.status\` | Read agent lane, live state, steward chain, dry-run result, Runner availability. | Seat or tenant-admin | Call with \`{"seat_id":"<seat-id>"}\`. |
49368
- | \`rost_list_agent_fleet\` | \`agent.list_fleet\` | Read every staffed agent seat at once: lane, live state, last real turn, 24h/7d real turns, measurable status, escalations, and 7-day spend, plus the liveness/health/work-progress axes the web fleet page renders (liveness state, health status, work evidence, host-resource pressure). | Tenant | Call with \`{}\`; counts use the same seat-run association as \`agent.list_runs\`, with sandbox dry runs excluded from real turns. |
49447
+ | \`rost_list_agent_fleet\` | \`agent.list_fleet\` | Read every staffed agent seat at once: lane, live state, last real turn, 24h/7d real turns, measurable status, escalations, and 7-day spend, plus the liveness/health/work-progress axes the web fleet page renders (liveness state, health status, work evidence, host-resource pressure), plus an \`is_system\` marker for Forge system seats. | Tenant | Call with \`{}\`; counts use the same seat-run association as \`agent.list_runs\`, with sandbox dry runs excluded from real turns. |
49369
49448
  | \`rost_get_dogfood_fleet_health_digest\` | \`agent.fleet_digest\` | Capture the daily dogfood fleet-health digest: live/idle state, 24h/7d turns, spend, recent failed runs, unresolved errors, failed notifications, and next actions. | Tenant | Call with \`{}\`; optional \`window_hours\` bounds recent failed-run evidence, while \`error_limit\` caps linked evidence per seat. |
49370
49449
  | \`rost_system_health\` | \`system.health\` | Read the scoped system-health snapshot across agent runs, unresolved errors, Signals, Cascade setup, work loop, integrations, runners, notifications, and Sync Brief readiness. | Tenant, member, or seat token | Call with \`{}\`; optional \`seat_id\` narrows inside the caller's server-derived scope. |
49371
49450
  | \`rost_get_run_heartbeats\` | \`agent.run_heartbeats\` | Read the per-seat run-heartbeat roll-up (quiet-but-healthy vs stalled) for long-running agent programs. | Tenant | Call with \`{}\`; optional \`seat_ids\` narrows to specific seats. |
@@ -49437,7 +49516,7 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
49437
49516
  | \`rost_update_model_gateway_policy\` | \`tenant.model_gateway_policy.update\` | Unlock or relock managed models that lack a zero-retention (ZDR) tier. | Tenant-admin | Owner-only, human-gated; call with \`{"allow_non_zdr_models":true}\` (or \`false\` to relock). Unlocking establishes an ADR-0018 \xA76 standing authorization and surfaces a per-model retention disclosure. |
49438
49517
  | \`rost_get_aicos_brain_settings\` | \`aicos.brain_settings.get\` | Read AICOS lane and brain labels for cloud managed/BYOK, runner Claude/Codex, and MCP client. | Tenant | Call with \`{}\`; returns enum labels only, never keys, vault refs, or runner secrets. |
49439
49518
  | \`rost_update_aicos_brain_settings\` | \`aicos.brain_settings.update\` | Update AICOS lane and cloud/runner brain preferences. | Tenant-admin | Owner-only and human-gated; call with \`{"lane":"runner","runner_brain":"claude-cli"}\` or \`{"cloud_brain":"byok"}\`. BYOK requires an active tenant Anthropic key. For AICOS, selecting \`codex-cli\` is currently rejected until the governed interactive runner path is verified. |
49440
- | \`rost_list_signals\` | \`signal.list\` | List measurables with their latest reading and on/off-track state. | Seat or tenant-admin | Call with \`{}\` to find measurable ids. |
49519
+ | \`rost_list_signals\` | \`signal.list\` | List measurables with their latest reading, on/off-track state, and \`effective_source\` (read-time measured-by). | Seat or tenant-admin | Call with \`{}\` to find measurable ids. |
49441
49520
  | \`rost_get_signal\` | \`signal.get\` | Read a measurable with its full reading history. | Seat or tenant-admin | Call with \`{"measurable_id":"<id>"}\`. |
49442
49521
  | \`rost_confirm_signal_reading\` | \`signal.confirm_reading\` | Confirm an unconfirmed reading as human-verified. | Seat or tenant-admin | Humans confirm; call with \`{"reading_id":"<id>"}\`. |
49443
49522
  | \`rost_correct_signal_reading\` | \`signal.correct_reading\` | Overwrite a reading with a human-confirmed value. | Seat or tenant-admin | Manual correction; expect confirmation. |
@@ -50174,7 +50253,7 @@ Agents can suggest commitments and report progress. They should not create a new
50174
50253
  order: 61,
50175
50254
  title: "Signal guide",
50176
50255
  summary: "How to define and read measurables so the company runs on evidence instead of status theater.",
50177
- version: "2026-07-18.3",
50256
+ version: "2026-07-20.2",
50178
50257
  public: true,
50179
50258
  audiences: ["human", "cli", "mcp", "in_app_agent"],
50180
50259
  stages: ["operating_rhythm"],
@@ -50229,11 +50308,11 @@ Avoid vanity numbers, manual-only status fields, and metrics nobody can act on.
50229
50308
 
50230
50309
  ## Operate Signal from CLI or MCP
50231
50310
 
50232
- - Read: \`{{cli}} signal list --json\` / \`signal.list\` / \`rost_list_signals\` returns measurables with their latest reading and on/off-track state. \`signal.get\` / \`rost_get_signal\` returns one measurable's full reading history.
50311
+ - Read: \`{{cli}} signal list --json\` / \`signal.list\` / \`rost_list_signals\` returns measurables with their latest reading and on/off-track state, plus \`effective_source\` \u2014 the read-time measured-by (\`auto\` when a source binding is active, otherwise \`agent\`/\`human\`), matching the \`/signal\` UI and distinct from the declared \`source\`. \`signal.get\` / \`rost_get_signal\` returns one measurable's full reading history.
50233
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.
50234
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).
50235
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.
50236
- - 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.
50237
50316
 
50238
50317
  ## Run Signal without an agent
50239
50318
 
@@ -53707,7 +53786,7 @@ function renderSignalList(output) {
53707
53786
  }
53708
53787
  return signals.map((signal) => {
53709
53788
  const record2 = asRecord(signal);
53710
- return `${field(record2, "measurable_id")} ${field(record2, "state")} ${field(record2, "name")} (latest ${field(record2, "latest_value")} / target ${field(record2, "target")})`;
53789
+ return `${field(record2, "measurable_id")} ${field(record2, "state")} ${field(record2, "name")} (latest ${field(record2, "latest_value")} / target ${field(record2, "target")}, measured by ${field(record2, "effective_source")})`;
53711
53790
  }).join("\n");
53712
53791
  }
53713
53792
  function renderSignalGet(output) {
@@ -57658,6 +57737,7 @@ ${usage()}
57658
57737
  let failed = false;
57659
57738
  let activeSessions = 0;
57660
57739
  const activeTurnExecutionIds = /* @__PURE__ */ new Set();
57740
+ let serverHeartbeatContractVersion = null;
57661
57741
  do {
57662
57742
  const detectedCapabilities = await detectCapabilities(cliVersion, env, homeDir, config2.maxSessionsOverride);
57663
57743
  const capabilities = capabilitiesForConfiguredRuntime(detectedCapabilities, config2.runtime);
@@ -57666,7 +57746,8 @@ ${usage()}
57666
57746
  const heartbeat = await post2(fetchImpl, options.appUrl, "/api/runner/heartbeat", buildHeartbeatBody({
57667
57747
  capabilities,
57668
57748
  telemetry: detectTelemetry(capabilities, env, activeSessions, cliVersion),
57669
- activeTurnExecutionIds: [...activeTurnExecutionIds]
57749
+ activeTurnExecutionIds: [...activeTurnExecutionIds],
57750
+ serverContractVersion: serverHeartbeatContractVersion
57670
57751
  }), state.runner_secret);
57671
57752
  if (heartbeat.status !== 200) {
57672
57753
  options.io.stderr.write(`heartbeat ${heartbeat.status}: ${redactForLog(JSON.stringify(heartbeat.json))}
@@ -57674,6 +57755,10 @@ ${usage()}
57674
57755
  failed = true;
57675
57756
  } else {
57676
57757
  heartbeatSucceeded = true;
57758
+ const learnedVersion = readHeartbeatContractVersion(heartbeat.json);
57759
+ if (learnedVersion !== null) {
57760
+ serverHeartbeatContractVersion = learnedVersion;
57761
+ }
57677
57762
  options.io.stdout.write(`heartbeat ok #${beat} runner_id=${state.runner_id} claude=${capabilities.claude.installed} codex=${capabilities.codex.installed}
57678
57763
  `);
57679
57764
  }
@@ -57687,7 +57772,11 @@ ${usage()}
57687
57772
  capabilities,
57688
57773
  io: options.io,
57689
57774
  telemetry: () => detectTelemetry(capabilities, env, activeSessions, cliVersion),
57690
- activeTurnIds: () => [...activeTurnExecutionIds]
57775
+ activeTurnIds: () => [...activeTurnExecutionIds],
57776
+ serverContractVersion: () => serverHeartbeatContractVersion,
57777
+ recordServerContractVersion: (v) => {
57778
+ serverHeartbeatContractVersion = v;
57779
+ }
57691
57780
  });
57692
57781
  try {
57693
57782
  await claimAndExecute(fetchImpl, options.appUrl, state, config2, options.io, localRuntime, activeTurnExecutionIds);
@@ -57712,13 +57801,17 @@ ${usage()}
57712
57801
  localRuntime,
57713
57802
  capabilities,
57714
57803
  () => detectTelemetry(capabilities, env, activeSessions, cliVersion),
57715
- activeTurnExecutionIds
57804
+ activeTurnExecutionIds,
57805
+ () => serverHeartbeatContractVersion,
57806
+ (v) => {
57807
+ serverHeartbeatContractVersion = v;
57808
+ }
57716
57809
  );
57717
57810
  }
57718
57811
  } while (!config2.once);
57719
57812
  return heartbeatSucceeded && !failed ? 0 : 1;
57720
57813
  }
57721
- 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) {
57722
57815
  const deadline = Date.now() + config2.heartbeatMs;
57723
57816
  const stopWait = runtime ? startTurnHeartbeat({
57724
57817
  fetchImpl,
@@ -57728,7 +57821,9 @@ async function waitForNextHeartbeat(fetchImpl, appUrl2, state, config2, io, runt
57728
57821
  capabilities,
57729
57822
  io,
57730
57823
  telemetry,
57731
- activeTurnIds: () => [...activeTurnExecutionIds]
57824
+ activeTurnIds: () => [...activeTurnExecutionIds],
57825
+ serverContractVersion,
57826
+ recordServerContractVersion
57732
57827
  }) : null;
57733
57828
  try {
57734
57829
  while (Date.now() < deadline) {
@@ -57748,12 +57843,17 @@ async function waitForNextHeartbeat(fetchImpl, appUrl2, state, config2, io, runt
57748
57843
  stopWait?.();
57749
57844
  }
57750
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
+ }
57751
57851
  function buildHeartbeatBody(input) {
57752
57852
  const body = {
57753
57853
  capabilities: input.capabilities,
57754
57854
  telemetry: input.telemetry
57755
57855
  };
57756
- if (input.activeTurnExecutionIds.length > 0) {
57856
+ if (input.activeTurnExecutionIds.length > 0 && (input.serverContractVersion ?? 0) >= HEARTBEAT_ACTIVE_TURN_MIN_CONTRACT_VERSION) {
57757
57857
  body.active_turn_execution_ids = input.activeTurnExecutionIds;
57758
57858
  }
57759
57859
  return body;
@@ -57774,11 +57874,17 @@ function startTurnHeartbeat(deps) {
57774
57874
  const response = await post2(deps.fetchImpl, deps.appUrl, "/api/runner/heartbeat", buildHeartbeatBody({
57775
57875
  capabilities: deps.capabilities,
57776
57876
  telemetry: deps.telemetry(),
57777
- activeTurnExecutionIds: deps.activeTurnIds()
57877
+ activeTurnExecutionIds: deps.activeTurnIds(),
57878
+ serverContractVersion: deps.serverContractVersion()
57778
57879
  }), deps.secret, controller.signal);
57779
57880
  if (!stopped && response.status !== 200) {
57780
57881
  deps.io.stderr.write(`turn heartbeat ${response.status}: ${redactForLog(JSON.stringify(response.json))}
57781
57882
  `);
57883
+ } else if (!stopped && response.status === 200) {
57884
+ const learnedVersion = readHeartbeatContractVersion(response.json);
57885
+ if (learnedVersion !== null) {
57886
+ deps.recordServerContractVersion(learnedVersion);
57887
+ }
57782
57888
  }
57783
57889
  } catch (error51) {
57784
57890
  if (!stopped) {