@rosthq/cli 0.7.100 → 0.7.102

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
@@ -44776,6 +44776,28 @@ var signalReadingProposalSchema = external_exports.object({
44776
44776
  }
44777
44777
  });
44778
44778
 
44779
+ // ../../packages/protocol/src/signal-draft-first-readings.ts
44780
+ var uuidSchema17 = external_exports.string().uuid();
44781
+ var signalDraftFirstReadingsInputSchema = external_exports.object({
44782
+ measurable_id: uuidSchema17
44783
+ }).strict();
44784
+ var signalDraftFirstReadingsOutputSchema = external_exports.object({
44785
+ measurable_id: uuidSchema17,
44786
+ // The live pull outcome, surfaced verbatim so the UI can explain a no-datapoint
44787
+ // result honestly ("source polled, nothing came back") rather than silently.
44788
+ outcome: external_exports.enum(["ok", "blocked", "error"]),
44789
+ // 0 or 1: a single-value recipe pull yields at most one current-period reading.
44790
+ // Never fabricates back-dated history the source cannot actually produce.
44791
+ drafted: external_exports.number().int().min(0).max(1),
44792
+ // Present only when drafted === 1.
44793
+ reading_id: uuidSchema17.optional(),
44794
+ value: external_exports.number().finite().optional(),
44795
+ period_start: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/, "expected YYYY-MM-DD").optional(),
44796
+ // Present only when drafted === 0: a short, non-secret machine reason
44797
+ // (e.g. "non_numeric", "blocked", "error") the UI maps to guidance.
44798
+ reason: external_exports.string().min(1).max(200).optional()
44799
+ }).strict();
44800
+
44779
44801
  // ../../packages/protocol/src/secret-broker.ts
44780
44802
  var uuid10 = external_exports.string().uuid();
44781
44803
  var VAULT_REF_PROVIDER = /^(?:infisical|doppler|local-dev):\/\/\S+$/;
@@ -46330,7 +46352,7 @@ var softwareFactoryBudgetExhaustedEventSchema = external_exports.object({
46330
46352
  }).strict();
46331
46353
 
46332
46354
  // ../../packages/protocol/src/sync-brief.ts
46333
- var uuidSchema17 = external_exports.string().uuid();
46355
+ var uuidSchema18 = external_exports.string().uuid();
46334
46356
  var dateSchema = external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/);
46335
46357
  var syncBriefGapSchema = external_exports.object({
46336
46358
  section: external_exports.enum(["exceptions", "goal_deltas", "task_review", "friction", "agent_activity"]),
@@ -46343,8 +46365,8 @@ var sectionSchema = (itemSchema) => external_exports.object({
46343
46365
  flagged_gaps: external_exports.array(syncBriefGapSchema)
46344
46366
  }).strict();
46345
46367
  var syncBriefExceptionSchema = external_exports.object({
46346
- measurable_id: uuidSchema17,
46347
- seat_id: uuidSchema17,
46368
+ measurable_id: uuidSchema18,
46369
+ seat_id: uuidSchema18,
46348
46370
  seat_name: external_exports.string().min(1),
46349
46371
  name: external_exports.string().min(1),
46350
46372
  state: external_exports.enum(["risk", "crit", "pending"]),
@@ -46355,8 +46377,8 @@ var syncBriefExceptionSchema = external_exports.object({
46355
46377
  freshness: external_exports.enum(["fresh", "due", "stale", "awaiting_first_reading", "not_expected"]).optional()
46356
46378
  }).strict();
46357
46379
  var syncBriefGoalDeltaSchema = external_exports.object({
46358
- goal_id: uuidSchema17,
46359
- seat_id: uuidSchema17.nullable(),
46380
+ goal_id: uuidSchema18,
46381
+ seat_id: uuidSchema18.nullable(),
46360
46382
  seat_name: external_exports.string().nullable(),
46361
46383
  title: external_exports.string().min(1),
46362
46384
  status: external_exports.enum(["on", "off", "done", "dropped"]),
@@ -46370,22 +46392,22 @@ var syncBriefTaskReviewSchema = external_exports.object({
46370
46392
  previous_done_rate: external_exports.number().min(0).max(1),
46371
46393
  delta: external_exports.number().min(-1).max(1),
46372
46394
  stalled: external_exports.array(external_exports.object({
46373
- task_id: uuidSchema17,
46374
- owner_seat_id: uuidSchema17,
46395
+ task_id: uuidSchema18,
46396
+ owner_seat_id: uuidSchema18,
46375
46397
  owner_seat_name: external_exports.string().min(1),
46376
46398
  title: external_exports.string().min(1),
46377
46399
  due_on: dateSchema,
46378
46400
  stalled_at: external_exports.string().nullable()
46379
46401
  }).strict()),
46380
46402
  overdue_by_owner: external_exports.array(external_exports.object({
46381
- owner_seat_id: uuidSchema17,
46403
+ owner_seat_id: uuidSchema18,
46382
46404
  owner_seat_name: external_exports.string().min(1),
46383
46405
  overdue_count: external_exports.number().int().nonnegative()
46384
46406
  }).strict())
46385
46407
  }).strict();
46386
46408
  var syncBriefIssueSchema = external_exports.object({
46387
- issue_id: uuidSchema17,
46388
- raised_by_seat_id: uuidSchema17,
46409
+ issue_id: uuidSchema18,
46410
+ raised_by_seat_id: uuidSchema18,
46389
46411
  raised_by_seat_name: external_exports.string().min(1),
46390
46412
  summary: external_exports.string().min(1),
46391
46413
  severity: external_exports.enum(["low", "med", "high", "critical"]),
@@ -46402,15 +46424,15 @@ var syncBriefActionItemTrendSchema = external_exports.object({
46402
46424
  has_prior: external_exports.boolean()
46403
46425
  }).strict();
46404
46426
  var syncBriefCascadeAtRiskSchema = external_exports.object({
46405
- goal_id: uuidSchema17,
46427
+ goal_id: uuidSchema18,
46406
46428
  title: external_exports.string().min(1),
46407
- seat_id: uuidSchema17.nullable(),
46429
+ seat_id: uuidSchema18.nullable(),
46408
46430
  progress_pct: external_exports.number().int().min(0).max(100),
46409
46431
  classification: external_exports.enum(["at_risk", "projected_miss"]),
46410
46432
  behind_days: external_exports.number().nullable()
46411
46433
  }).strict();
46412
46434
  var syncBriefRunHeartbeatStalledSeatSchema = external_exports.object({
46413
- seat_id: uuidSchema17,
46435
+ seat_id: uuidSchema18,
46414
46436
  current_phase: external_exports.string().nullable(),
46415
46437
  stalled_run_count: external_exports.number().int().nonnegative()
46416
46438
  }).strict();
@@ -46423,8 +46445,8 @@ var syncBriefRunHeartbeatRollupSchema = external_exports.object({
46423
46445
  stalled_seats: external_exports.array(syncBriefRunHeartbeatStalledSeatSchema)
46424
46446
  }).strict();
46425
46447
  var syncBriefAgentActivitySchema = external_exports.object({
46426
- agent_id: uuidSchema17,
46427
- seat_id: uuidSchema17,
46448
+ agent_id: uuidSchema18,
46449
+ seat_id: uuidSchema18,
46428
46450
  seat_name: external_exports.string().min(1),
46429
46451
  run_count: external_exports.number().int().nonnegative(),
46430
46452
  failed_run_count: external_exports.number().int().nonnegative(),
@@ -46437,11 +46459,11 @@ var syncBriefAgentActivitySchema = external_exports.object({
46437
46459
  var syncBriefSchema = external_exports.object({
46438
46460
  schema_version: external_exports.literal(1),
46439
46461
  tenant: external_exports.object({
46440
- id: uuidSchema17,
46462
+ id: uuidSchema18,
46441
46463
  name: external_exports.string().min(1)
46442
46464
  }).strict(),
46443
46465
  cluster: external_exports.object({
46444
- id: uuidSchema17,
46466
+ id: uuidSchema18,
46445
46467
  name: external_exports.string().min(1)
46446
46468
  }).strict().nullable(),
46447
46469
  period: external_exports.object({
@@ -46558,11 +46580,11 @@ var usageSnapshotOutputSchema = external_exports.object({
46558
46580
  }).strict();
46559
46581
 
46560
46582
  // ../../packages/protocol/src/event-payloads.ts
46561
- var uuidSchema18 = external_exports.string().uuid();
46583
+ var uuidSchema19 = external_exports.string().uuid();
46562
46584
  var evidenceSchema = external_exports.array(external_exports.unknown());
46563
46585
  var statusEventPayloadSchema = external_exports.object({
46564
46586
  measurables: external_exports.array(external_exports.object({
46565
- id: uuidSchema18,
46587
+ id: uuidSchema19,
46566
46588
  value: external_exports.number().finite(),
46567
46589
  // DER-1045: an optional, non-secret citation for an agent-proposed reading
46568
46590
  // (signal.report) — where the number came from + the agent's confidence.
@@ -46591,13 +46613,13 @@ var statusEventPayloadSchema = external_exports.object({
46591
46613
  }).strict().optional()
46592
46614
  }).strict()).optional(),
46593
46615
  goals: external_exports.array(external_exports.object({
46594
- id: uuidSchema18,
46616
+ id: uuidSchema19,
46595
46617
  state: external_exports.enum(["on", "off"]),
46596
46618
  note: external_exports.string().min(1).optional()
46597
46619
  }).strict()).optional()
46598
46620
  }).strict();
46599
46621
  var escalationCategorySchema = external_exports.enum(["safety_refusal"]);
46600
- var escalationEventPayloadSchema = external_exports.object({
46622
+ var escalationRaiseInputSchema = external_exports.object({
46601
46623
  reason: external_exports.string().min(1),
46602
46624
  charter_clause: external_exports.string().min(1),
46603
46625
  evidence: evidenceSchema,
@@ -46607,14 +46629,105 @@ var escalationEventPayloadSchema = external_exports.object({
46607
46629
  // explicitly escalated with a category (today: a safety refusal).
46608
46630
  category: escalationCategorySchema.optional()
46609
46631
  }).strict();
46632
+ var escalationEventPayloadSchema = escalationRaiseInputSchema.extend({
46633
+ run_id: external_exports.string().uuid().optional()
46634
+ }).strict();
46610
46635
  var issueEventPayloadSchema = external_exports.object({
46611
46636
  summary: external_exports.string().min(1),
46612
- blocking_goal_id: uuidSchema18.optional(),
46613
- broken_measurable_id: uuidSchema18.optional(),
46637
+ blocking_goal_id: uuidSchema19.optional(),
46638
+ broken_measurable_id: uuidSchema19.optional(),
46614
46639
  evidence: evidenceSchema,
46615
46640
  severity: external_exports.enum(["low", "med", "high", "critical"])
46616
46641
  }).strict();
46617
46642
 
46643
+ // ../../packages/protocol/src/event-internal-payloads.ts
46644
+ var uuidSchema20 = external_exports.string().uuid();
46645
+ var internalEventObjectSchema = external_exports.record(external_exports.string(), external_exports.unknown()).refine((payload) => !Array.isArray(payload), "Internal event payload must be an object");
46646
+ var actionEventPayloadSchema = external_exports.object({ action: external_exports.string().min(1) }).passthrough();
46647
+ function authoritativePrefixUnion(branches, authoritative) {
46648
+ return external_exports.union(branches).superRefine((payload, ctx) => {
46649
+ const action = payload.action;
46650
+ if (typeof action !== "string") return;
46651
+ for (const { prefix, strict } of authoritative) {
46652
+ if (!action.startsWith(prefix)) continue;
46653
+ const result = strict.safeParse(payload);
46654
+ if (!result.success) {
46655
+ ctx.addIssue({
46656
+ code: external_exports.ZodIssueCode.custom,
46657
+ message: `Malformed "${action}" payload: an action beginning with "${prefix}" must satisfy the strict ${prefix}* schema and may not fall through to a permissive branch (${result.error.issues.map((issue2) => issue2.message).join("; ")})`
46658
+ });
46659
+ }
46660
+ return;
46661
+ }
46662
+ });
46663
+ }
46664
+ var inviteCreatedEventPayloadSchema = external_exports.object({
46665
+ action: external_exports.literal("invite.created"),
46666
+ invite_id: uuidSchema20,
46667
+ email: external_exports.string().email(),
46668
+ role: memberRoleSchema,
46669
+ member_type: memberTypeSchema,
46670
+ access_profile: memberAccessProfileSchema,
46671
+ aicos_access_scope: aicosAccessScopeSchema,
46672
+ invited_by: uuidSchema20
46673
+ }).strict();
46674
+ var inviteRevokedEventPayloadSchema = external_exports.object({
46675
+ action: external_exports.literal("invite.revoked"),
46676
+ invite_id: uuidSchema20,
46677
+ revoked_by: uuidSchema20
46678
+ }).strict();
46679
+ var inviteAcceptedEventPayloadSchema = external_exports.object({
46680
+ action: external_exports.literal("invite.accepted"),
46681
+ invite_id: uuidSchema20,
46682
+ user_id: uuidSchema20,
46683
+ membership_id: uuidSchema20,
46684
+ linked_seat_ids: external_exports.array(uuidSchema20)
46685
+ }).strict();
46686
+ var inviteLifecycleEventPayloadSchema = external_exports.union([
46687
+ inviteCreatedEventPayloadSchema,
46688
+ inviteRevokedEventPayloadSchema,
46689
+ inviteAcceptedEventPayloadSchema
46690
+ ]);
46691
+ var internalEventWriterPayloadSchema = authoritativePrefixUnion(
46692
+ [
46693
+ inviteCreatedEventPayloadSchema,
46694
+ inviteRevokedEventPayloadSchema,
46695
+ inviteAcceptedEventPayloadSchema,
46696
+ actionEventPayloadSchema,
46697
+ internalEventObjectSchema
46698
+ ],
46699
+ [{ prefix: "invite.", strict: inviteLifecycleEventPayloadSchema }]
46700
+ );
46701
+ var provisioningEventPayloadSchema = authoritativePrefixUnion(
46702
+ [
46703
+ inviteCreatedEventPayloadSchema,
46704
+ inviteRevokedEventPayloadSchema,
46705
+ inviteAcceptedEventPayloadSchema,
46706
+ actionEventPayloadSchema,
46707
+ internalEventObjectSchema
46708
+ ],
46709
+ // Only "invite." is authoritative today (the sole prefix with typed branches).
46710
+ // Non-invite provisioning actions (e.g. tenant.created) keep riding the
46711
+ // permissive branches unchanged, preserving parse-compat with stored rows.
46712
+ [{ prefix: "invite.", strict: inviteLifecycleEventPayloadSchema }]
46713
+ );
46714
+ var decisionEventPayloadSchema = external_exports.union([
46715
+ actionEventPayloadSchema,
46716
+ internalEventObjectSchema
46717
+ ]);
46718
+ var systemEventPayloadSchema = external_exports.union([
46719
+ actionEventPayloadSchema,
46720
+ internalEventObjectSchema
46721
+ ]);
46722
+ var charterEventPayloadSchema = external_exports.union([
46723
+ actionEventPayloadSchema,
46724
+ internalEventObjectSchema
46725
+ ]);
46726
+ var taskEventPayloadSchema = external_exports.union([
46727
+ actionEventPayloadSchema,
46728
+ internalEventObjectSchema
46729
+ ]);
46730
+
46618
46731
  // ../../packages/protocol/src/held-tool-action.ts
46619
46732
  var heldToolActionReplaySchema = external_exports.object({
46620
46733
  tool_name: external_exports.string().min(1),
@@ -46623,14 +46736,14 @@ var heldToolActionReplaySchema = external_exports.object({
46623
46736
  }).strict();
46624
46737
 
46625
46738
  // ../../packages/protocol/src/operating.ts
46626
- var uuidSchema19 = external_exports.string().uuid();
46739
+ var uuidSchema21 = external_exports.string().uuid();
46627
46740
  var taskListInputSchema = external_exports.object({}).strict();
46628
46741
  var operatingTaskSchema = external_exports.object({
46629
- id: uuidSchema19,
46742
+ id: uuidSchema21,
46630
46743
  title: external_exports.string(),
46631
46744
  description: external_exports.string().nullable(),
46632
- from_seat_id: uuidSchema19.nullable(),
46633
- goal_id: uuidSchema19.nullable(),
46745
+ from_seat_id: uuidSchema21.nullable(),
46746
+ goal_id: uuidSchema21.nullable(),
46634
46747
  acceptance_criteria: external_exports.unknown(),
46635
46748
  due_on: external_exports.string().nullable(),
46636
46749
  status: external_exports.string()
@@ -46639,54 +46752,54 @@ var taskListOutputSchema = external_exports.object({
46639
46752
  tasks: external_exports.array(operatingTaskSchema)
46640
46753
  }).strict();
46641
46754
  var taskAcceptInputSchema = external_exports.object({
46642
- id: uuidSchema19
46755
+ id: uuidSchema21
46643
46756
  }).strict();
46644
46757
  var taskConfirmProposalInputSchema = external_exports.object({
46645
- id: uuidSchema19
46758
+ id: uuidSchema21
46646
46759
  }).strict();
46647
46760
  var taskMutationOutputSchema = external_exports.object({
46648
46761
  task: external_exports.object({
46649
- id: uuidSchema19,
46762
+ id: uuidSchema21,
46650
46763
  status: external_exports.string()
46651
46764
  }).strict()
46652
46765
  }).strict();
46653
46766
  var taskDeclineInputSchema = external_exports.object({
46654
- id: uuidSchema19,
46767
+ id: uuidSchema21,
46655
46768
  reason: external_exports.string().min(1)
46656
46769
  }).strict();
46657
46770
  var taskDeclineOutputSchema = external_exports.object({
46658
46771
  task: external_exports.object({
46659
- id: uuidSchema19,
46772
+ id: uuidSchema21,
46660
46773
  status: external_exports.string(),
46661
46774
  decline_reason: external_exports.string()
46662
46775
  }).strict()
46663
46776
  }).strict();
46664
46777
  var taskCompleteInputSchema = external_exports.object({
46665
- id: uuidSchema19,
46778
+ id: uuidSchema21,
46666
46779
  summary: external_exports.string().min(1),
46667
46780
  artifacts: external_exports.unknown().optional()
46668
46781
  }).strict();
46669
46782
  var taskCompleteOutputSchema = external_exports.object({
46670
- task_id: uuidSchema19,
46671
- event_id: uuidSchema19,
46783
+ task_id: uuidSchema21,
46784
+ event_id: uuidSchema21,
46672
46785
  type: external_exports.literal("handoff")
46673
46786
  }).strict();
46674
46787
  var statusRecordOutputSchema = external_exports.object({
46675
- event_id: uuidSchema19,
46788
+ event_id: uuidSchema21,
46676
46789
  type: external_exports.literal("status")
46677
46790
  }).strict();
46678
46791
  var frictionFileIssueInputSchema = issueEventPayloadSchema.extend({
46679
46792
  source_key: external_exports.string().trim().min(1).max(200).optional()
46680
46793
  }).strict();
46681
46794
  var duplicateFrictionCandidateSchema = external_exports.object({
46682
- issue_id: uuidSchema19,
46795
+ issue_id: uuidSchema21,
46683
46796
  summary: external_exports.string().min(1),
46684
46797
  // pg_trgm similarity in [0, 1]; higher is more similar.
46685
46798
  similarity: external_exports.number().min(0).max(1)
46686
46799
  }).strict();
46687
46800
  var frictionFileIssueOutputSchema = external_exports.object({
46688
- event_id: uuidSchema19,
46689
- issue_id: uuidSchema19,
46801
+ event_id: uuidSchema21,
46802
+ issue_id: uuidSchema21,
46690
46803
  type: external_exports.literal("issue"),
46691
46804
  // DER-1522: likely-duplicate candidates detected before creation. Recommend,
46692
46805
  // never block — filing always proceeds; empty when nothing crossed the
@@ -46700,12 +46813,12 @@ var workLogInputSchema = external_exports.object({
46700
46813
  note: external_exports.string().min(1)
46701
46814
  }).strict();
46702
46815
  var workLogOutputSchema = external_exports.object({
46703
- event_id: uuidSchema19,
46816
+ event_id: uuidSchema21,
46704
46817
  type: external_exports.literal("task")
46705
46818
  }).strict();
46706
46819
  var escalationRaiseOutputSchema = external_exports.object({
46707
- event_id: uuidSchema19,
46708
- escalation_id: uuidSchema19,
46820
+ event_id: uuidSchema21,
46821
+ escalation_id: uuidSchema21,
46709
46822
  type: external_exports.literal("escalation")
46710
46823
  }).strict();
46711
46824
  var deliverableKindSchema2 = external_exports.enum(["brief", "work_evidence", "artifact", "report", "other"]);
@@ -46764,7 +46877,7 @@ var deliverableRejectOutputSchema = external_exports.object({
46764
46877
  }).strict();
46765
46878
 
46766
46879
  // ../../packages/protocol/src/steward.ts
46767
- var uuidSchema20 = external_exports.string().uuid();
46880
+ var uuidSchema22 = external_exports.string().uuid();
46768
46881
  var escalationStatusSchema = external_exports.enum([
46769
46882
  "open",
46770
46883
  "approved",
@@ -46773,18 +46886,18 @@ var escalationStatusSchema = external_exports.enum([
46773
46886
  ]);
46774
46887
  var stewardReplayActionSummarySchema = external_exports.object({
46775
46888
  tool_name: external_exports.string(),
46776
- run_id: uuidSchema20
46889
+ run_id: uuidSchema22
46777
46890
  }).strict();
46778
46891
  var stewardProposedToolCallSchema = external_exports.object({
46779
46892
  tool_name: external_exports.string(),
46780
46893
  args_summary: external_exports.unknown()
46781
46894
  }).strict();
46782
46895
  var stewardEscalationSchema = external_exports.object({
46783
- id: uuidSchema20,
46784
- seat_id: uuidSchema20,
46896
+ id: uuidSchema22,
46897
+ seat_id: uuidSchema22,
46785
46898
  seat_name: external_exports.string(),
46786
46899
  seat_type: external_exports.enum(["human", "agent", "hybrid"]),
46787
- steward_seat_id: uuidSchema20,
46900
+ steward_seat_id: uuidSchema22,
46788
46901
  steward_seat_name: external_exports.string(),
46789
46902
  reason: external_exports.string(),
46790
46903
  charter_clause: external_exports.string(),
@@ -46803,11 +46916,11 @@ var stewardEscalationSchema = external_exports.object({
46803
46916
  proposed_tool_call: stewardProposedToolCallSchema.optional(),
46804
46917
  recommended_action: external_exports.string().nullable(),
46805
46918
  status: escalationStatusSchema,
46806
- decided_by: uuidSchema20.nullable(),
46919
+ decided_by: uuidSchema22.nullable(),
46807
46920
  decided_by_name: external_exports.string().nullable(),
46808
46921
  decided_at: external_exports.string().nullable(),
46809
46922
  decision_note: external_exports.string().nullable(),
46810
- issue_id: uuidSchema20.nullable(),
46923
+ issue_id: uuidSchema22.nullable(),
46811
46924
  created_at: external_exports.string(),
46812
46925
  updated_at: external_exports.string()
46813
46926
  }).strict();
@@ -46818,27 +46931,27 @@ var escalationListOutputSchema = external_exports.object({
46818
46931
  escalations: external_exports.array(stewardEscalationSchema)
46819
46932
  }).strict();
46820
46933
  var escalationGetInputSchema = external_exports.object({
46821
- id: uuidSchema20
46934
+ id: uuidSchema22
46822
46935
  }).strict();
46823
46936
  var escalationGetOutputSchema = external_exports.object({
46824
46937
  escalation: stewardEscalationSchema
46825
46938
  }).strict();
46826
46939
  var escalationResolveActionSchema = external_exports.enum(["approve", "convert_to_issue"]);
46827
46940
  var escalationResolveInputSchema = external_exports.object({
46828
- id: uuidSchema20,
46941
+ id: uuidSchema22,
46829
46942
  action: escalationResolveActionSchema.optional(),
46830
46943
  rationale: external_exports.string().trim().min(1).optional()
46831
46944
  }).strict();
46832
46945
  var escalationRejectInputSchema = external_exports.object({
46833
- id: uuidSchema20,
46946
+ id: uuidSchema22,
46834
46947
  rationale: external_exports.string().trim().min(1).optional()
46835
46948
  }).strict();
46836
46949
  var escalationDecisionOutputSchema = external_exports.object({
46837
- escalation_id: uuidSchema20,
46950
+ escalation_id: uuidSchema22,
46838
46951
  status: escalationStatusSchema,
46839
- decision_id: uuidSchema20,
46840
- decided_by: uuidSchema20,
46841
- issue_id: uuidSchema20.nullable(),
46952
+ decision_id: uuidSchema22,
46953
+ decided_by: uuidSchema22,
46954
+ issue_id: uuidSchema22.nullable(),
46842
46955
  // DER-1478: true when the resolved escalation carries a durable replay_action
46843
46956
  // (an approval-gated connector write) that the approvals path should actuate
46844
46957
  // after this decision is recorded. Omitted (not false) for non-connector
@@ -47019,7 +47132,7 @@ var workDetailsSchemas = {
47019
47132
  };
47020
47133
 
47021
47134
  // ../../packages/protocol/src/agent-grant-intervention.ts
47022
- var uuidSchema21 = external_exports.string().uuid();
47135
+ var uuidSchema23 = external_exports.string().uuid();
47023
47136
  var agentGrantBlockerKindSchema = external_exports.enum([
47024
47137
  "rost_capability",
47025
47138
  "os_permission",
@@ -47036,16 +47149,16 @@ var agentGrantInterventionStatusSchema = external_exports.enum([
47036
47149
  ]);
47037
47150
  var agentGrantKindSchema = external_exports.enum(["allow_once", "permanent"]);
47038
47151
  var agentGrantInterventionSchema = external_exports.object({
47039
- id: uuidSchema21,
47040
- seat_id: uuidSchema21,
47152
+ id: uuidSchema23,
47153
+ seat_id: uuidSchema23,
47041
47154
  seat_name: external_exports.string().nullable(),
47042
- agent_id: uuidSchema21.nullable(),
47155
+ agent_id: uuidSchema23.nullable(),
47043
47156
  // The source blocked attempt (audit link); null once the run is retention-purged.
47044
- run_id: uuidSchema21.nullable(),
47157
+ run_id: uuidSchema23.nullable(),
47045
47158
  // The runner the blocker lives on, for os_permission repairs ("Open on Runner").
47046
- runner_id: uuidSchema21.nullable(),
47159
+ runner_id: uuidSchema23.nullable(),
47047
47160
  // The linked continuation attempt, populated when a continuation is enqueued.
47048
- continuation_run_id: uuidSchema21.nullable(),
47161
+ continuation_run_id: uuidSchema23.nullable(),
47049
47162
  blocker_kind: agentGrantBlockerKindSchema,
47050
47163
  status: agentGrantInterventionStatusSchema,
47051
47164
  // The specific capability/scope requested (e.g. "gmail.send"), when applicable.
@@ -47060,7 +47173,7 @@ var agentGrantInterventionSchema = external_exports.object({
47060
47173
  // True once the blocked attempt released its lease/compute (AC #2 clean termination).
47061
47174
  compute_released: external_exports.boolean(),
47062
47175
  grant_kind: agentGrantKindSchema.nullable(),
47063
- decided_by: uuidSchema21.nullable(),
47176
+ decided_by: uuidSchema23.nullable(),
47064
47177
  decided_by_name: external_exports.string().nullable(),
47065
47178
  decided_at: external_exports.string().nullable(),
47066
47179
  expires_at: external_exports.string().nullable(),
@@ -47069,7 +47182,7 @@ var agentGrantInterventionSchema = external_exports.object({
47069
47182
  }).strict();
47070
47183
  var agentGrantInterventionListInputSchema = external_exports.object({
47071
47184
  // Optional seat filter (a seat-scoped caller is additionally clamped to its own seat).
47072
- seat_id: uuidSchema21.optional(),
47185
+ seat_id: uuidSchema23.optional(),
47073
47186
  // When true, include decided/terminal records (declined/resumed/expired) too;
47074
47187
  // by default only the actionable open blocked/granted records are returned.
47075
47188
  include_decided: external_exports.boolean().optional()
@@ -47078,14 +47191,14 @@ var agentGrantInterventionListOutputSchema = external_exports.object({
47078
47191
  interventions: external_exports.array(agentGrantInterventionSchema)
47079
47192
  }).strict();
47080
47193
  var agentGrantInterventionGetInputSchema = external_exports.object({
47081
- id: uuidSchema21
47194
+ id: uuidSchema23
47082
47195
  }).strict();
47083
47196
  var agentGrantInterventionGetOutputSchema = external_exports.object({
47084
47197
  intervention: agentGrantInterventionSchema
47085
47198
  }).strict();
47086
47199
  var agentGrantInterventionDecideActionSchema = external_exports.enum(["grant", "decline"]);
47087
47200
  var agentGrantInterventionDecideInputSchema = external_exports.object({
47088
- intervention_id: uuidSchema21,
47201
+ intervention_id: uuidSchema23,
47089
47202
  action: agentGrantInterventionDecideActionSchema,
47090
47203
  // Defaults to allow_once. `permanent` never auto-widens — it returns
47091
47204
  // requires_charter_resign so the surface routes the human to a Charter re-sign.
@@ -47097,12 +47210,12 @@ var agentGrantInterventionDecideInputSchema = external_exports.object({
47097
47210
  rationale: external_exports.string().trim().min(1).max(2e3).optional()
47098
47211
  }).strict();
47099
47212
  var agentGrantInterventionDecideOutputSchema = external_exports.object({
47100
- intervention_id: uuidSchema21,
47213
+ intervention_id: uuidSchema23,
47101
47214
  status: agentGrantInterventionStatusSchema,
47102
- continuation_run_id: uuidSchema21.nullable(),
47215
+ continuation_run_id: uuidSchema23.nullable(),
47103
47216
  // The human decision row written for a grant/decline (invariant #7). Null only
47104
47217
  // when a permanent grant deferred to a Charter re-sign (no one-time decision made).
47105
- decision_id: uuidSchema21.nullable(),
47218
+ decision_id: uuidSchema23.nullable(),
47106
47219
  // True when the human chose the permanent option — the surface must route to a
47107
47220
  // Charter re-sign; no authority was widened by this call (invariant #10).
47108
47221
  requires_charter_resign: external_exports.boolean(),
@@ -47116,12 +47229,12 @@ var agentGrantInterventionDecideOutputSchema = external_exports.object({
47116
47229
  enqueue_failed: external_exports.boolean()
47117
47230
  }).strict();
47118
47231
  var agentGrantInterventionResumeInputSchema = external_exports.object({
47119
- intervention_id: uuidSchema21
47232
+ intervention_id: uuidSchema23
47120
47233
  }).strict();
47121
47234
  var agentGrantInterventionResumeOutputSchema = external_exports.object({
47122
- intervention_id: uuidSchema21,
47235
+ intervention_id: uuidSchema23,
47123
47236
  status: agentGrantInterventionStatusSchema,
47124
- continuation_run_id: uuidSchema21.nullable(),
47237
+ continuation_run_id: uuidSchema23.nullable(),
47125
47238
  enqueue_failed: external_exports.boolean()
47126
47239
  }).strict();
47127
47240
 
@@ -47139,7 +47252,7 @@ var eventTypes = [
47139
47252
  "task"
47140
47253
  ];
47141
47254
  var publicMessageTypes = ["status", "handoff", "escalation", "issue"];
47142
- var uuidSchema22 = external_exports.string().uuid();
47255
+ var uuidSchema24 = external_exports.string().uuid();
47143
47256
  var evidenceSchema2 = external_exports.array(external_exports.unknown());
47144
47257
  var internalEventPayloadSchema = external_exports.record(external_exports.string(), external_exports.unknown()).refine((payload) => !Array.isArray(payload), "Internal event payload must be an object");
47145
47258
  var handoffEventPayloadSchema = external_exports.object({
@@ -47149,24 +47262,24 @@ var handoffEventPayloadSchema = external_exports.object({
47149
47262
  acceptance_criteria: external_exports.array(external_exports.string().min(1)),
47150
47263
  due: external_exports.string().datetime({ offset: true })
47151
47264
  }).strict(),
47152
- to_seat_id: uuidSchema22
47265
+ to_seat_id: uuidSchema24
47153
47266
  }).strict();
47154
47267
  var intakeEventPayloadSchema = external_exports.discriminatedUnion("action", [
47155
47268
  external_exports.object({
47156
47269
  action: external_exports.literal("uploaded"),
47157
- document_id: uuidSchema22,
47270
+ document_id: uuidSchema24,
47158
47271
  storage_ref: external_exports.string().min(1)
47159
47272
  }).strict(),
47160
47273
  external_exports.object({
47161
47274
  action: external_exports.literal("parsed"),
47162
- document_id: uuidSchema22,
47275
+ document_id: uuidSchema24,
47163
47276
  mode: external_exports.enum(["source_tree", "function_first"]),
47164
47277
  seat_count: external_exports.number().int().nonnegative(),
47165
47278
  edge_count: external_exports.number().int().nonnegative()
47166
47279
  }).strict(),
47167
47280
  external_exports.object({
47168
47281
  action: external_exports.literal("failed"),
47169
- document_id: uuidSchema22,
47282
+ document_id: uuidSchema24,
47170
47283
  reason: external_exports.string().min(1)
47171
47284
  }).strict(),
47172
47285
  external_exports.object({
@@ -47272,9 +47385,9 @@ var publicMessageTypeSchema = external_exports.enum(publicMessageTypes);
47272
47385
  var eventEnvelopeBaseSchema = external_exports.object({
47273
47386
  protocol: external_exports.literal(EVENT_PROTOCOL),
47274
47387
  type: eventTypeSchema,
47275
- seat_id: uuidSchema22.nullable(),
47388
+ seat_id: uuidSchema24.nullable(),
47276
47389
  occurred_at: external_exports.string().datetime({ offset: true }),
47277
- goal_ancestry: external_exports.array(uuidSchema22)
47390
+ goal_ancestry: external_exports.array(uuidSchema24)
47278
47391
  }).strict();
47279
47392
  var statusEventEnvelopeSchema = eventEnvelopeBaseSchema.extend({
47280
47393
  type: external_exports.literal("status"),
@@ -47298,7 +47411,7 @@ var intakeEventEnvelopeSchema = eventEnvelopeBaseSchema.extend({
47298
47411
  }).strict();
47299
47412
  var internalEventEnvelopeSchema = eventEnvelopeBaseSchema.extend({
47300
47413
  type: external_exports.enum(["decision", "provisioning", "system", "charter", "task"]),
47301
- payload: internalEventPayloadSchema
47414
+ payload: internalEventWriterPayloadSchema
47302
47415
  }).strict();
47303
47416
  var eventEnvelopeSchema = external_exports.discriminatedUnion("type", [
47304
47417
  statusEventEnvelopeSchema,
@@ -48530,7 +48643,7 @@ External connectors are being rolled out provider by provider, conservatively (r
48530
48643
  order: 48,
48531
48644
  title: "CLI and MCP installation guide",
48532
48645
  summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
48533
- version: "2026-07-18.2",
48646
+ version: "2026-07-18.3",
48534
48647
  public: true,
48535
48648
  audiences: ["human", "cli", "mcp", "in_app_agent"],
48536
48649
  stages: ["company_setup", "staffing"],
@@ -49368,6 +49481,7 @@ Seat-scoped MCP tokens expose the operating protocol below. The server still che
49368
49481
  | \`rost_file_issue\` | \`friction.file_issue\` | File a Friction issue. | Seat | Call with title, severity, and impact. Agent/scheduled producers may pass an optional \`source_key\` for deterministic idempotency \u2014 a repeat filing with the same key returns the existing open issue with \`already_filed:true\` and creates nothing new. |
49369
49482
  | \`rost_log_work\` | \`work.log\` | Record a lightweight free-text work breadcrumb for the seat. For structured, per-entity, queryable business activity (a client might ask "which ones?"), use \`rost_record_work\` instead. | Seat | Call with summary and evidence. |
49370
49483
  | \`rost_report_a_signal_reading\` | \`signal.report\` | Propose one draft reading for this seat's own agent-sourced measurable. The reading lands unconfirmed for human review. | Seat | Call with \`{"measurable_id":"<id>","value":42,"note":"read off the source","confidence":"high"}\`. |
49484
+ | \`rost_draft_first_readings_from_a_connected_source\` | \`signal.draft_first_readings\` | Pull this seat's measurable's connected source ONCE (read-only, SSRF-guarded) and land the value as a DRAFT reading for the current period, for a human to confirm. For a measurable that has a source but no confirmed reading yet. Never publishes and never fabricates a value \u2014 a non-numeric or blocked pull drafts nothing. | Seat | Call with \`{"measurable_id":"<id>"}\`. |
49371
49485
  | \`rost_create_agent_deliverable\` | \`deliverable.create\` | Create a scrubbed durable work output for the acting seat. | Seat | Call with title, kind, summary/content, and optional safe links. |
49372
49486
  | \`rost_attach_agent_deliverable\` | \`deliverable.attach\` | Attach a scrubbed deliverable to a source run, task, or work order after server-side seat validation. | Seat | Call with title, kind, and the source ids that belong to the acting seat. |
49373
49487
  | \`rost_record_work\` | \`work.record\` | Append structured business-activity records to the seat's append-only work ledger, in a batch, right after each action succeeds. Prefer this over \`rost_log_work\` for durable, per-entity, queryable activity (verb + entity ref + outcome + evidence). Secret-redacted; idempotent on \`dedup_key\`; corrections are superseding records. | Seat | Call with \`{"records":[{"verb":"contacted","object":{"kind":"supplier","external_id":"sup_42","label":"Acme Foods"},"summary":"Emailed Acme for a monk fruit quote.","dedup_key":"gmail:msg-abc"}]}\`. |
@@ -49942,7 +50056,7 @@ Agents can suggest commitments and report progress. They should not create a new
49942
50056
  order: 61,
49943
50057
  title: "Signal guide",
49944
50058
  summary: "How to define and read measurables so the company runs on evidence instead of status theater.",
49945
- version: "2026-07-12.1",
50059
+ version: "2026-07-18.3",
49946
50060
  public: true,
49947
50061
  audiences: ["human", "cli", "mcp", "in_app_agent"],
49948
50062
  stages: ["operating_rhythm"],
@@ -49955,6 +50069,7 @@ Agents can suggest commitments and report progress. They should not create a new
49955
50069
  "signal.confirm_reading",
49956
50070
  "signal.correct_reading",
49957
50071
  "signal.report",
50072
+ "signal.draft_first_readings",
49958
50073
  "measurable.create",
49959
50074
  "signal.import",
49960
50075
  "measurable_template.list",
@@ -50000,6 +50115,7 @@ Avoid vanity numbers, manual-only status fields, and metrics nobody can act on.
50000
50115
  - 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.
50001
50116
  - 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).
50002
50117
  - 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.
50118
+ - 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.
50003
50119
 
50004
50120
  ## Run Signal without an agent
50005
50121
 
@@ -50523,7 +50639,7 @@ Agents may explain which setting is needed and why. They should not ask users to
50523
50639
  order: 73,
50524
50640
  title: "Members and invites guide",
50525
50641
  summary: "How to invite humans into the company without confusing user access with seat accountability.",
50526
- version: "2026-07-03.2",
50642
+ version: "2026-07-18.1",
50527
50643
  public: true,
50528
50644
  audiences: ["human", "cli", "mcp", "in_app_agent"],
50529
50645
  stages: ["company_setup", "staffing"],
@@ -50548,7 +50664,7 @@ Invite the person as a tenant member. Then assign them to one or more seats only
50548
50664
 
50549
50665
  Tenant roles control app access. Seats control operating accountability. Do not attach goals, tasks, or measurables directly to a user when the work belongs to a seat.
50550
50666
 
50551
- Owners and admins can invite people and manage member roles. Use \`external_implementer\` plus setup-limited access for a temporary implementation partner helping configure onboarding, Responsibility Graph, Compass, Charters, and agent setup. Setup-limited access is intentionally not enough for sensitive operating surfaces: Settings member/invite/token/runner/credential metadata, run and tool-call history, runner diagnostics, integration metadata and tests, system health, Signal operating data, and owner/operating pages require owner/admin or full setup access. Do not grant external implementers sensitive run history, hidden tool arguments, credentials, Memory, or company-operating data unless an owner explicitly expands their profile.
50667
+ Owners and admins can invite people and manage member roles. Use \`external_implementer\` plus setup-limited access for a temporary implementation partner helping configure onboarding, Responsibility Graph, Compass, Charters, and agent setup. Setup-limited access is intentionally not enough for sensitive operating surfaces: Settings member/invite/token/runner/credential metadata, run and tool-call history, runner diagnostics, integration metadata and tests, system health, and owner/operating pages require owner/admin or full setup access. Scorecard (Signal) and Issues (Friction) reads are the exception: every active member can open them, scoped by the tenant member-visibility mode \u2014 either every member sees all Signals and Frictions, or each member sees only the seats they occupy plus everything downstream. Write actions on those pages stay role-gated. Do not grant external implementers sensitive run history, hidden tool arguments, credentials, Memory, or company-operating data unless an owner explicitly expands their profile.
50552
50668
 
50553
50669
  ## Working across several client companies
50554
50670
 
@@ -52751,6 +52867,7 @@ var COMMAND_MANIFEST = [
52751
52867
  { "id": "signal.bind_confirm", "namespace": "signal", "action": "bind_confirm", "title": "Confirm and activate a signal binding", "description": "Human-only activation of a draft signal binding (draft \u2192 active). Agents can never self-activate \u2014 an agent caller produces a pending confirmation. A drives_status binding requires a second-anchor confirmation. Once active, the scheduled pull auto-fills the measurable.", "requiredScope": "tenant", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "binding_id", "flag": "binding-id", "type": "string", "required": true }], "hasComplexInput": true, "help": "Human-only: activate a draft signal binding (draft -> active) so scheduled pulls begin; an agent that calls it produces a pending confirmation." },
52752
52868
  { "id": "signal.confirm_reading", "namespace": "signal", "action": "confirm_reading", "title": "Confirm Signal reading", "description": "Confirm an unconfirmed reading as human-verified. Humans confirm; agents cannot. Idempotent on an already-confirmed reading.", "requiredScope": "seat", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "reading_id", "flag": "reading-id", "type": "string", "required": true }], "hasComplexInput": false, "help": "Confirm an unconfirmed reading as human-verified; confirmation is a human act, not an agent one." },
52753
52869
  { "id": "signal.correct_reading", "namespace": "signal", "action": "correct_reading", "title": "Correct Signal reading", "description": "Overwrite a reading for a period with a human-confirmed value. A manual correction is a human decision and is gated behind confirmation.", "requiredScope": "seat", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "measurable_id", "flag": "measurable-id", "type": "string", "required": true }, { "name": "period_start", "flag": "period-start", "type": "string", "required": true }, { "name": "value", "flag": "value", "type": "number", "required": true }], "hasComplexInput": false, "help": "Correct a reading only as a human; manual corrections that change confirmed data require confirmation." },
52870
+ { "id": "signal.draft_first_readings", "namespace": "signal", "action": "draft_first_readings", "title": "Draft first readings from a connected source", "description": "Pull a measurable's connected source ONCE (read-only, SSRF-guarded) and land the value as a DRAFT reading for the current period, for a human to confirm. For a measurable that has a source but no confirmed reading yet. Never publishes a reading and never fabricates a value \u2014 a non-numeric or blocked pull drafts nothing.", "requiredScope": "seat", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "measurable_id", "flag": "measurable-id", "type": "string", "required": true }], "hasComplexInput": false, "help": "Pull a measurable's connected source once (read-only, SSRF-guarded) and land the value as a DRAFT reading for the current period, for a human to confirm with signal.confirm_reading. For a measurable that has a source but no confirmed reading yet. Never publishes and never fabricates a value \u2014 a non-numeric or blocked pull drafts nothing.", "example": { "measurable_id": "01900001-0001-7001-8001-000000000001" } },
52754
52871
  { "id": "signal.get", "namespace": "signal", "action": "get", "title": "Get Signal", "description": "Return a measurable definition with its full reading history (confirmed and unconfirmed).", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "measurable_id", "flag": "measurable-id", "type": "string", "required": true }], "hasComplexInput": false, "help": "Read a measurable's full reading history before confirming or correcting a value." },
52755
52872
  { "id": "signal.import", "namespace": "signal", "action": "import", "title": "Import Signals from CSV", "description": "Bulk-import measurable definitions and their historical readings from a parsed scorecard export (e.g. a ninety CSV). Owners are seat names resolved to seats; readings are human-confirmed. Idempotent \u2014 re-import upserts by (measurable, period, cadence). Re-importing a measurable at a different cadence is rejected, not overwritten.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "dry_run", "flag": "dry-run", "type": "boolean", "required": false }], "hasComplexInput": true, "help": "Bulk-import measurables + historical readings from a parsed scorecard export (owners resolve to seats); idempotent, human-confirmed, re-import upserts by period." },
52756
52873
  { "id": "signal.list", "namespace": "signal", "action": "list", "title": "List Signals", "description": "List measurable definitions with their latest reading and on/off-track state. Seat-scoped callers see only their own seat.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "seat_id", "flag": "seat-id", "type": "string", "required": false }], "hasComplexInput": false, "help": "List Signals to find measurable ids and current on/off-track state before recording or correcting readings." },
@@ -57419,15 +57536,17 @@ ${usage()}
57419
57536
  let heartbeatSucceeded = false;
57420
57537
  let failed = false;
57421
57538
  let activeSessions = 0;
57539
+ const activeTurnExecutionIds = /* @__PURE__ */ new Set();
57422
57540
  do {
57423
57541
  const detectedCapabilities = await detectCapabilities(cliVersion, env, homeDir);
57424
57542
  const capabilities = capabilitiesForConfiguredRuntime(detectedCapabilities, config2.runtime);
57425
57543
  const localRuntime = config2.execute ? effectiveExecutionRuntime(config2.runtime, capabilities) : null;
57426
57544
  try {
57427
- const heartbeat = await post2(fetchImpl, options.appUrl, "/api/runner/heartbeat", {
57545
+ const heartbeat = await post2(fetchImpl, options.appUrl, "/api/runner/heartbeat", buildHeartbeatBody({
57428
57546
  capabilities,
57429
- telemetry: detectTelemetry(capabilities, env, activeSessions, cliVersion)
57430
- }, state.runner_secret);
57547
+ telemetry: detectTelemetry(capabilities, env, activeSessions, cliVersion),
57548
+ activeTurnExecutionIds: [...activeTurnExecutionIds]
57549
+ }), state.runner_secret);
57431
57550
  if (heartbeat.status !== 200) {
57432
57551
  options.io.stderr.write(`heartbeat ${heartbeat.status}: ${redactForLog(JSON.stringify(heartbeat.json))}
57433
57552
  `);
@@ -57446,10 +57565,11 @@ ${usage()}
57446
57565
  intervalMs: config2.heartbeatMs,
57447
57566
  capabilities,
57448
57567
  io: options.io,
57449
- telemetry: () => detectTelemetry(capabilities, env, activeSessions, cliVersion)
57568
+ telemetry: () => detectTelemetry(capabilities, env, activeSessions, cliVersion),
57569
+ activeTurnIds: () => [...activeTurnExecutionIds]
57450
57570
  });
57451
57571
  try {
57452
- await claimAndExecute(fetchImpl, options.appUrl, state, config2, options.io, localRuntime);
57572
+ await claimAndExecute(fetchImpl, options.appUrl, state, config2, options.io, localRuntime, activeTurnExecutionIds);
57453
57573
  } finally {
57454
57574
  stopTurnHeartbeat();
57455
57575
  activeSessions = Math.max(0, activeSessions - 1);
@@ -57462,27 +57582,61 @@ ${usage()}
57462
57582
  }
57463
57583
  beat += 1;
57464
57584
  if (!config2.once) {
57465
- await waitForNextHeartbeat(fetchImpl, options.appUrl, state, config2, options.io, localRuntime);
57585
+ await waitForNextHeartbeat(
57586
+ fetchImpl,
57587
+ options.appUrl,
57588
+ state,
57589
+ config2,
57590
+ options.io,
57591
+ localRuntime,
57592
+ capabilities,
57593
+ () => detectTelemetry(capabilities, env, activeSessions, cliVersion),
57594
+ activeTurnExecutionIds
57595
+ );
57466
57596
  }
57467
57597
  } while (!config2.once);
57468
57598
  return heartbeatSucceeded && !failed ? 0 : 1;
57469
57599
  }
57470
- async function waitForNextHeartbeat(fetchImpl, appUrl2, state, config2, io, runtime) {
57600
+ async function waitForNextHeartbeat(fetchImpl, appUrl2, state, config2, io, runtime, capabilities, telemetry, activeTurnExecutionIds) {
57471
57601
  const deadline = Date.now() + config2.heartbeatMs;
57472
- while (Date.now() < deadline) {
57473
- const intervalMs = Math.min(config2.interactiveClaimMs, deadline - Date.now());
57474
- await sleep3(intervalMs);
57475
- if (!runtime || Date.now() >= deadline) {
57476
- continue;
57477
- }
57478
- try {
57479
- await claimAndExecute(fetchImpl, appUrl2, state, config2, io, runtime, { interactiveOnly: true });
57480
- } catch (error51) {
57481
- io.stderr.write(`transient runner error (continuing): ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
57602
+ const stopWait = runtime ? startTurnHeartbeat({
57603
+ fetchImpl,
57604
+ appUrl: appUrl2,
57605
+ secret: state.runner_secret,
57606
+ intervalMs: config2.heartbeatMs,
57607
+ capabilities,
57608
+ io,
57609
+ telemetry,
57610
+ activeTurnIds: () => [...activeTurnExecutionIds]
57611
+ }) : null;
57612
+ try {
57613
+ while (Date.now() < deadline) {
57614
+ const intervalMs = Math.min(config2.interactiveClaimMs, deadline - Date.now());
57615
+ await sleep3(intervalMs);
57616
+ if (!runtime || Date.now() >= deadline) {
57617
+ continue;
57618
+ }
57619
+ try {
57620
+ await claimAndExecute(fetchImpl, appUrl2, state, config2, io, runtime, activeTurnExecutionIds, { interactiveOnly: true });
57621
+ } catch (error51) {
57622
+ io.stderr.write(`transient runner error (continuing): ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
57482
57623
  `);
57624
+ }
57483
57625
  }
57626
+ } finally {
57627
+ stopWait?.();
57484
57628
  }
57485
57629
  }
57630
+ function buildHeartbeatBody(input) {
57631
+ const body = {
57632
+ capabilities: input.capabilities,
57633
+ telemetry: input.telemetry
57634
+ };
57635
+ if (input.activeTurnExecutionIds.length > 0) {
57636
+ body.active_turn_execution_ids = input.activeTurnExecutionIds;
57637
+ }
57638
+ return body;
57639
+ }
57486
57640
  function startTurnHeartbeat(deps) {
57487
57641
  let stopped = false;
57488
57642
  let inFlight = false;
@@ -57496,10 +57650,11 @@ function startTurnHeartbeat(deps) {
57496
57650
  timeout.unref?.();
57497
57651
  void (async () => {
57498
57652
  try {
57499
- const response = await post2(deps.fetchImpl, deps.appUrl, "/api/runner/heartbeat", {
57653
+ const response = await post2(deps.fetchImpl, deps.appUrl, "/api/runner/heartbeat", buildHeartbeatBody({
57500
57654
  capabilities: deps.capabilities,
57501
- telemetry: deps.telemetry()
57502
- }, deps.secret, controller.signal);
57655
+ telemetry: deps.telemetry(),
57656
+ activeTurnExecutionIds: deps.activeTurnIds()
57657
+ }), deps.secret, controller.signal);
57503
57658
  if (!stopped && response.status !== 200) {
57504
57659
  deps.io.stderr.write(`turn heartbeat ${response.status}: ${redactForLog(JSON.stringify(response.json))}
57505
57660
  `);
@@ -57960,7 +58115,7 @@ async function post2(fetchImpl, appUrl2, pathName, body, token, signal) {
57960
58115
  }
57961
58116
  return { status: response.status, json: json2 };
57962
58117
  }
57963
- async function claimAndExecute(fetchImpl, appUrl2, state, config2, io, runtime, options = {}) {
58118
+ async function claimAndExecute(fetchImpl, appUrl2, state, config2, io, runtime, activeTurnIds, options = {}) {
57964
58119
  const claimStartedAt = (/* @__PURE__ */ new Date()).toISOString();
57965
58120
  const claimed = await post2(
57966
58121
  fetchImpl,
@@ -57980,7 +58135,7 @@ async function claimAndExecute(fetchImpl, appUrl2, state, config2, io, runtime,
57980
58135
  });
57981
58136
  const turnExecution = claimed.json.turn_execution;
57982
58137
  if (claimed.status === 200 && turnExecution && typeof turnExecution === "object") {
57983
- await executeClaimedUnit(fetchImpl, appUrl2, state, config2, io, runtime, ledger, {
58138
+ await executeClaimedUnit(fetchImpl, appUrl2, state, config2, io, runtime, ledger, activeTurnIds, {
57984
58139
  kind: "turn_execution",
57985
58140
  idField: "turn_execution_id",
57986
58141
  item: turnExecution,
@@ -57994,7 +58149,7 @@ async function claimAndExecute(fetchImpl, appUrl2, state, config2, io, runtime,
57994
58149
  if (claimed.status !== 200 || !workOrder || typeof workOrder !== "object") {
57995
58150
  return;
57996
58151
  }
57997
- await executeClaimedUnit(fetchImpl, appUrl2, state, config2, io, runtime, ledger, {
58152
+ await executeClaimedUnit(fetchImpl, appUrl2, state, config2, io, runtime, ledger, activeTurnIds, {
57998
58153
  kind: "work_order",
57999
58154
  idField: "work_order_id",
58000
58155
  item: workOrder,
@@ -58003,7 +58158,7 @@ async function claimAndExecute(fetchImpl, appUrl2, state, config2, io, runtime,
58003
58158
  logRef: (id) => `runner-local:${id}`
58004
58159
  });
58005
58160
  }
58006
- async function executeClaimedUnit(fetchImpl, appUrl2, state, config2, io, runtime, ledger, input) {
58161
+ async function executeClaimedUnit(fetchImpl, appUrl2, state, config2, io, runtime, ledger, activeTurnIds, input) {
58007
58162
  const id = input.item.id;
58008
58163
  if (typeof id !== "string") {
58009
58164
  return;
@@ -58012,79 +58167,88 @@ async function executeClaimedUnit(fetchImpl, appUrl2, state, config2, io, runtim
58012
58167
  if (started.status !== 200) {
58013
58168
  throw new Error(`${claimedUnitLabel(input.kind)} start failed ${started.status}: ${redactForLog(JSON.stringify(started.json))}`);
58014
58169
  }
58015
- const turnRuntime = runtimeForClaimedUnit(input.item, runtime, input.kind);
58016
- const result = turnRuntime ? await runLocalTurn({ fetchImpl, appUrl: appUrl2, state, config: config2, io, ledger }, input.item, turnRuntime, input.kind) : {
58017
- ok: false,
58018
- summary: `Runner runtime ${runtime} cannot execute requested ${String(input.item.brain ?? "unknown")} turn.`
58019
- };
58020
- if (input.kind === "turn_execution" && turnRuntime === "claude") {
58021
- try {
58022
- if (result.ok && result.claudeSessionId) {
58023
- await persistClaudeSessionId(config2.stateFile, state, input.item, result.claudeSessionId);
58024
- } else if (!result.ok && result.resumedFromSessionId) {
58025
- await clearClaudeSessionId(config2.stateFile, state, input.item);
58026
- }
58027
- } catch (error51) {
58028
- io.stderr.write(`resume cache update failed (continuing): ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
58170
+ if (input.kind === "turn_execution") {
58171
+ activeTurnIds.add(id);
58172
+ }
58173
+ try {
58174
+ const turnRuntime = runtimeForClaimedUnit(input.item, runtime, input.kind);
58175
+ const result = turnRuntime ? await runLocalTurn({ fetchImpl, appUrl: appUrl2, state, config: config2, io, ledger }, input.item, turnRuntime, input.kind) : {
58176
+ ok: false,
58177
+ summary: `Runner runtime ${runtime} cannot execute requested ${String(input.item.brain ?? "unknown")} turn.`
58178
+ };
58179
+ if (input.kind === "turn_execution" && turnRuntime === "claude") {
58180
+ try {
58181
+ if (result.ok && result.claudeSessionId) {
58182
+ await persistClaudeSessionId(config2.stateFile, state, input.item, result.claudeSessionId);
58183
+ } else if (!result.ok && result.resumedFromSessionId) {
58184
+ await clearClaudeSessionId(config2.stateFile, state, input.item);
58185
+ }
58186
+ } catch (error51) {
58187
+ io.stderr.write(`resume cache update failed (continuing): ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
58029
58188
  `);
58189
+ }
58030
58190
  }
58031
- }
58032
- const reportBody = {
58033
- status: result.ok ? "succeeded" : "failed",
58034
- // Cap to the result route's summary limit (see capResultSummary / RESULT_SUMMARY_MAX). A
58035
- // real turn summary can exceed it; without the cap the server's Zod `.max()` 400s the
58036
- // report, and because the loop treats a throw as "transient (continuing)" the phase
58037
- // zombies in `running` until its lease grace. Truncating keeps a successful turn a success.
58038
- summary: capResultSummary(result.summary),
58039
- log_ref: input.logRef(id),
58040
- model: (turnRuntime ?? runtime) === "codex" ? "codex-cli" : "claude-cli",
58041
- // DER-1915: token usage lifted from the claude JSON envelope (extractClaudeUsage), sent for
58042
- // BOTH kinds when present never defaulted to 0 (see RunnerTurnResult.inputTokens/outputTokens).
58043
- // Both result routes (work-orders and turn-executions) accept these snake_case fields.
58044
- ...result.inputTokens != null ? { input_tokens: result.inputTokens } : {},
58045
- ...result.outputTokens != null ? { output_tokens: result.outputTokens } : {},
58046
- // Artifact-bearing result fields (T1.5) only sent when the harness produced them, so
58047
- // the result route's `.strict()` schema still accepts a plain read-status report.
58048
- ...result.prUrl ? { pr_url: result.prUrl } : {},
58049
- ...result.branch ? { branch: result.branch } : {},
58050
- ...result.commitSha ? { commit_sha: result.commitSha } : {},
58051
- ...result.evidence && result.evidence.length > 0 ? { evidence: result.evidence } : {},
58052
- ...result.changedPaths && result.changedPaths.length > 0 ? { changed_paths: result.changedPaths } : {},
58053
- // DER-1352 (T1.8): the merge-turn postcondition. Only sent when the harness GET-verified the PR
58054
- // merged on GitHub the driver's completion writer marks the changeset merged + writes the
58055
- // decisions row ONLY on this. Gated on result.ok so a failed turn can never carry it.
58056
- ...result.ok && result.merged === true ? { merged: true } : {},
58057
- ...result.ok && result.merged === true && result.mergedCommitSha ? { merged_commit_sha: result.mergedCommitSha } : {},
58058
- // DER-1331 (T1.9): the step ledger + the model's (already redacted, bounded) transcript.
58059
- // Only the work-orders result route accepts these today (turn-executions is a separate,
58060
- // narrower endpoint T1.9 scope); its Zod schema strips unrecognized keys on a non-strict
58061
- // parse either way, so sending them there is harmless, just unused.
58062
- ...input.kind === "work_order" ? { step_ledger: ledger.entries() } : {},
58063
- ...input.kind === "work_order" && result.transcript ? { transcript: result.transcript } : {},
58064
- // DER-1371: the planner's structured decomposition only sent when the model produced one.
58065
- ...result.planArtifact ? { plan_artifact: result.planArtifact } : {}
58066
- };
58067
- const reported = await postResultWithRetries(fetchImpl, appUrl2, input.resultPath(id), reportBody, state.runner_secret);
58068
- if (reported.status !== 200) {
58069
- if (reported.status >= 400 && reported.status < 500) {
58070
- const fallback = await post2(fetchImpl, appUrl2, input.resultPath(id), {
58071
- status: "failed",
58072
- summary: capResultSummary(
58073
- `Runner result rejected by server (${reported.status}). Original outcome: ${result.ok ? "succeeded" : "failed"}. ${redactForLog(JSON.stringify(reported.json))}`
58074
- ),
58075
- log_ref: input.logRef(id),
58076
- model: (turnRuntime ?? runtime) === "codex" ? "codex-cli" : "claude-cli"
58077
- }, state.runner_secret);
58078
- if (fallback.status === 200) {
58079
- io.stdout.write(`${input.idField}=${id} status=failed (result rejected ${reported.status}; reported terminal failure)
58191
+ const reportBody = {
58192
+ status: result.ok ? "succeeded" : "failed",
58193
+ // Cap to the result route's summary limit (see capResultSummary / RESULT_SUMMARY_MAX). A
58194
+ // real turn summary can exceed it; without the cap the server's Zod `.max()` 400s the
58195
+ // report, and because the loop treats a throw as "transient (continuing)" the phase
58196
+ // zombies in `running` until its lease grace. Truncating keeps a successful turn a success.
58197
+ summary: capResultSummary(result.summary),
58198
+ log_ref: input.logRef(id),
58199
+ model: (turnRuntime ?? runtime) === "codex" ? "codex-cli" : "claude-cli",
58200
+ // DER-1915: token usage lifted from the claude JSON envelope (extractClaudeUsage), sent for
58201
+ // BOTH kinds when present never defaulted to 0 (see RunnerTurnResult.inputTokens/outputTokens).
58202
+ // Both result routes (work-orders and turn-executions) accept these snake_case fields.
58203
+ ...result.inputTokens != null ? { input_tokens: result.inputTokens } : {},
58204
+ ...result.outputTokens != null ? { output_tokens: result.outputTokens } : {},
58205
+ // Artifact-bearing result fields (T1.5) only sent when the harness produced them, so
58206
+ // the result route's `.strict()` schema still accepts a plain read-status report.
58207
+ ...result.prUrl ? { pr_url: result.prUrl } : {},
58208
+ ...result.branch ? { branch: result.branch } : {},
58209
+ ...result.commitSha ? { commit_sha: result.commitSha } : {},
58210
+ ...result.evidence && result.evidence.length > 0 ? { evidence: result.evidence } : {},
58211
+ ...result.changedPaths && result.changedPaths.length > 0 ? { changed_paths: result.changedPaths } : {},
58212
+ // DER-1352 (T1.8): the merge-turn postcondition. Only sent when the harness GET-verified the PR
58213
+ // merged on GitHub — the driver's completion writer marks the changeset merged + writes the
58214
+ // decisions row ONLY on this. Gated on result.ok so a failed turn can never carry it.
58215
+ ...result.ok && result.merged === true ? { merged: true } : {},
58216
+ ...result.ok && result.merged === true && result.mergedCommitSha ? { merged_commit_sha: result.mergedCommitSha } : {},
58217
+ // DER-1331 (T1.9): the step ledger + the model's (already redacted, bounded) transcript.
58218
+ // Only the work-orders result route accepts these today (turn-executions is a separate,
58219
+ // narrower endpoint T1.9 scope); its Zod schema strips unrecognized keys on a non-strict
58220
+ // parse either way, so sending them there is harmless, just unused.
58221
+ ...input.kind === "work_order" ? { step_ledger: ledger.entries() } : {},
58222
+ ...input.kind === "work_order" && result.transcript ? { transcript: result.transcript } : {},
58223
+ // DER-1371: the planner's structured decomposition only sent when the model produced one.
58224
+ ...result.planArtifact ? { plan_artifact: result.planArtifact } : {}
58225
+ };
58226
+ const reported = await postResultWithRetries(fetchImpl, appUrl2, input.resultPath(id), reportBody, state.runner_secret);
58227
+ if (reported.status !== 200) {
58228
+ if (reported.status >= 400 && reported.status < 500) {
58229
+ const fallback = await post2(fetchImpl, appUrl2, input.resultPath(id), {
58230
+ status: "failed",
58231
+ summary: capResultSummary(
58232
+ `Runner result rejected by server (${reported.status}). Original outcome: ${result.ok ? "succeeded" : "failed"}. ${redactForLog(JSON.stringify(reported.json))}`
58233
+ ),
58234
+ log_ref: input.logRef(id),
58235
+ model: (turnRuntime ?? runtime) === "codex" ? "codex-cli" : "claude-cli"
58236
+ }, state.runner_secret);
58237
+ if (fallback.status === 200) {
58238
+ io.stdout.write(`${input.idField}=${id} status=failed (result rejected ${reported.status}; reported terminal failure)
58080
58239
  `);
58081
- return;
58240
+ return;
58241
+ }
58082
58242
  }
58243
+ throw new Error(`${claimedUnitLabel(input.kind)} result failed ${reported.status}: ${redactForLog(JSON.stringify(reported.json))}`);
58083
58244
  }
58084
- throw new Error(`${claimedUnitLabel(input.kind)} result failed ${reported.status}: ${redactForLog(JSON.stringify(reported.json))}`);
58085
- }
58086
- io.stdout.write(`${input.idField}=${id} status=${result.ok ? "succeeded" : "failed"}
58245
+ io.stdout.write(`${input.idField}=${id} status=${result.ok ? "succeeded" : "failed"}
58087
58246
  `);
58247
+ } finally {
58248
+ if (input.kind === "turn_execution") {
58249
+ activeTurnIds.delete(id);
58250
+ }
58251
+ }
58088
58252
  }
58089
58253
  var RESULT_REPORT_RETRY_ATTEMPTS = 4;
58090
58254
  var RESULT_REPORT_RETRY_BASE_DELAY_MS = 500;