@getmarrow/install 0.1.32 → 0.1.33

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/README.md CHANGED
@@ -62,9 +62,9 @@ Required secret:
62
62
  export MARROW_API_KEY=mrw_live_...
63
63
  ```
64
64
 
65
- ## What's New in v0.1.32
65
+ ## What's New in v0.1.33
66
66
 
67
- v0.1.32 adds one machine-readable governance-fit contract across discovery, evidence, and integration surfaces while preserving the server-verified first-run activation introduced in v0.1.29:
67
+ v0.1.33 adds agent-disagreement visibility to the Fleet Operator TUI. Operators can see open and review-required arbitration receipts and inspect the exact next action produced through the existing Marrow runtime. It preserves the machine-readable governance-fit contract introduced in v0.1.32 and the server-verified first-run activation introduced in v0.1.29:
68
68
 
69
69
  - GitHub and npm now advertise separate signed discovery placements;
70
70
  - package metadata identifies the installer as agent governance rather than a general memory utility;
@@ -160,7 +160,7 @@ npx @getmarrow/install --repair
160
160
  npx @getmarrow/install fleet
161
161
  ```
162
162
 
163
- The fleet view shows live agents, active workflows, risky actions waiting for proof, stale or failed outcomes, capture health, recent decisions, and exact repair commands. It is an operator surface for the authenticated account, not a public status dashboard.
163
+ The fleet view shows live agents, active workflows, agent disagreements and their latest arbitration receipt, risky actions waiting for proof, stale or failed outcomes, capture health, recent decisions, and exact repair commands. Press Enter on **Agent disagreements** to inspect the bound decision, selected proposal, whether Marrow selected a proposal, synthesized a safe sequence, held the action for owner review, or blocked the conflicting actions. Review-required work must be approved from an authenticated Marrow dashboard session; the TUI does not let an agent approve itself. It is an operator surface for the authenticated account, not a public status dashboard.
164
164
 
165
165
  ## Integration Paths
166
166
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getmarrow/install",
3
- "version": "0.1.32",
3
+ "version": "0.1.33",
4
4
  "description": "Universal installer and governed runner for Marrow agent fleets.",
5
5
  "bin": {
6
6
  "marrow-install": "bin/marrow-install.js"
@@ -8,7 +8,7 @@ const readline = require('node:readline');
8
8
  const DEFAULT_BASE_URL = 'https://api.getmarrow.ai';
9
9
  const HIGH_RISK_TERMS = /\b(deploy|prod|production|publish|release|merge|migration|migrate|secret|token|key|cloudflare|wrangler|npm publish|gh pr merge|git push|terraform apply|kubectl apply|delete|destroy|drop)\b/i;
10
10
  const GOVERN_TUI_ROW_COUNT = 7;
11
- const FLEET_TUI_ROW_COUNT = 11;
11
+ const FLEET_TUI_ROW_COUNT = 12;
12
12
  function usage() {
13
13
  return `Usage:
14
14
  npx @getmarrow/install run --agent deploy-agent -- npm test
@@ -761,6 +761,38 @@ function normalizeGates(status, report) {
761
761
  };
762
762
  }
763
763
 
764
+ function normalizeArbitrations(status, report, fleet) {
765
+ const source = firstDefined(
766
+ report.arbitrations,
767
+ report.report?.arbitrations,
768
+ fleet.arbitrations,
769
+ status.arbitrations,
770
+ {},
771
+ ) || {};
772
+ const receipts = listValue(
773
+ source.receipts,
774
+ source.items,
775
+ report.recent_arbitrations,
776
+ fleet.recent_arbitrations,
777
+ status.recent_arbitrations,
778
+ ).slice(0, 8).map((receipt) => ({
779
+ id: displayText(firstDefined(receipt?.id, receipt?.receipt_id, 'arbitration'), 80),
780
+ decision_id: displayText(firstDefined(receipt?.decision_id, ''), 80),
781
+ resolution: statusLabel(receipt?.resolution, 'unknown'),
782
+ conflict_type: displayText(firstDefined(receipt?.conflict_type, 'action_conflict'), 60),
783
+ selected_proposal_id: displayText(firstDefined(receipt?.selected_proposal_id, ''), 80),
784
+ requesting_agent_id: displayText(firstDefined(receipt?.requesting_agent_id, ''), 80),
785
+ exact_next_action: displayText(firstDefined(receipt?.exact_next_action, ''), 180),
786
+ owner_approval_required: Boolean(receipt?.owner_approval_required),
787
+ created_at: displayText(firstDefined(receipt?.created_at, ''), 80),
788
+ }));
789
+ return {
790
+ open_count: numberValue(firstDefined(source.open_count, source.open, receipts.filter((item) => item.resolution !== 'selected' && item.resolution !== 'synthesized').length), 0),
791
+ review_required_count: numberValue(firstDefined(source.review_required_count, source.review_required, receipts.filter((item) => item.resolution === 'review_required').length), 0),
792
+ receipts,
793
+ };
794
+ }
795
+
764
796
  function normalizeFixCommands(status, capacity) {
765
797
  const commands = [];
766
798
  const add = (value, commandOnly = false) => {
@@ -840,6 +872,7 @@ function normalizeFleetSnapshot(raw, options) {
840
872
  ), raw.capacity?.ok ? 'ok' : 'unknown');
841
873
  const recentDecisions = normalizeRecentDecisions(status, report, fleet);
842
874
  const gates = normalizeGates(status, report);
875
+ const arbitrations = normalizeArbitrations(status, report, fleet);
843
876
  const fixCommands = normalizeFixCommands(status, capacity);
844
877
  const errors = Object.entries(raw)
845
878
  .filter(([, value]) => value && value.ok === false)
@@ -859,6 +892,7 @@ function normalizeFleetSnapshot(raw, options) {
859
892
  recent_decisions: recentDecisions,
860
893
  degraded_hooks: missedHooks,
861
894
  gates,
895
+ arbitrations,
862
896
  agents,
863
897
  fix_commands: fixCommands.length ? fixCommands : ['npx @getmarrow/install doctor'],
864
898
  source_errors: errors,
@@ -898,6 +932,10 @@ function fleetPanel(snapshot) {
898
932
  ? snapshot.agents.slice(0, 5).map((agent) => ` - ${agent.id}${agent.role ? ` (${agent.role})` : ''}: ${agent.status}${agent.last_seen_at ? ` last_seen=${agent.last_seen_at}` : ''}`).join('\n')
899
933
  : ' - no agent roster returned yet';
900
934
  const fixes = snapshot.fix_commands.map((command) => ` - ${command}`).join('\n');
935
+ const arbitration = snapshot.arbitrations.receipts[0];
936
+ const arbitrationSummary = arbitration
937
+ ? `${arbitration.resolution} (${arbitration.conflict_type})${arbitration.exact_next_action ? ` - ${arbitration.exact_next_action}` : ''}`
938
+ : 'none reported yet';
901
939
  return [
902
940
  'Marrow Fleet Operator',
903
941
  '',
@@ -906,6 +944,8 @@ function fleetPanel(snapshot) {
906
944
  '',
907
945
  `Live agents: ${snapshot.live_agents ?? 'unknown'}`,
908
946
  `Active workflows: ${snapshot.active_workflows ?? 'unknown'}`,
947
+ `Agent disagreements: open=${snapshot.arbitrations.open_count} review_required=${snapshot.arbitrations.review_required_count}`,
948
+ `Latest arbitration: ${arbitrationSummary}`,
909
949
  `Risky actions waiting for proof: ${snapshot.proof_waiting}`,
910
950
  `Failed/stale outcomes: ${snapshot.failed_stale_outcomes}`,
911
951
  `Backpressure/capacity status: ${snapshot.backpressure_status}${snapshot.capacity_next_action ? ` - ${snapshot.capacity_next_action}` : ''}`,
@@ -1324,6 +1364,7 @@ function renderFleetTui(state) {
1324
1364
  const gateSummary = `deploy=${snapshot.gates.deploy} publish=${snapshot.gates.publish} merge=${snapshot.gates.merge}`;
1325
1365
  const fixCommand = snapshot.fix_commands[0] || 'npx @getmarrow/install doctor';
1326
1366
  const recent = snapshot.recent_decisions[0] || 'none reported yet';
1367
+ const arbitration = snapshot.arbitrations.receipts[0];
1327
1368
  const rows = [
1328
1369
  {
1329
1370
  label: 'Live agents',
@@ -1335,6 +1376,13 @@ function renderFleetTui(state) {
1335
1376
  value: String(snapshot.active_workflows ?? 'unknown'),
1336
1377
  hint: 'Current account-scoped workflow pressure from Marrow.',
1337
1378
  },
1379
+ {
1380
+ label: 'Agent disagreements',
1381
+ value: `open=${snapshot.arbitrations.open_count} review_required=${snapshot.arbitrations.review_required_count}`,
1382
+ hint: arbitration
1383
+ ? `${arbitration.resolution}: ${arbitration.exact_next_action || arbitration.conflict_type}`
1384
+ : 'No conflicting fleet proposals reported.',
1385
+ },
1338
1386
  {
1339
1387
  label: 'Risky actions waiting for proof',
1340
1388
  value: String(snapshot.proof_waiting),
@@ -1651,31 +1699,36 @@ async function runFleetInteractive(options, input = process.stdin, output = proc
1651
1699
  state.status = 'Agent selection changed.';
1652
1700
  render();
1653
1701
  } else if (key.name === 'return') {
1654
- if (state.cursor === 9) {
1702
+ if (state.cursor === 10) {
1655
1703
  cleanup();
1656
1704
  output.write(`\n${state.snapshot.fix_commands[0] || 'npx @getmarrow/install doctor'}\n`);
1657
1705
  resolve();
1658
1706
  return;
1659
1707
  }
1660
- if (state.cursor === 10) {
1708
+ if (state.cursor === 11) {
1661
1709
  cleanup();
1662
1710
  resolve();
1663
1711
  return;
1664
1712
  }
1665
1713
  const selectedAgent = state.snapshot.agents[state.agentIndex];
1666
- if (state.cursor === 0 || state.cursor === 8) {
1714
+ if (state.cursor === 0 || state.cursor === 9) {
1667
1715
  state.lastResult = selectedAgent
1668
1716
  ? `Agent ${selectedAgent.id}: status=${selectedAgent.status}${selectedAgent.role ? ` role=${selectedAgent.role}` : ''}${selectedAgent.last_seen_at ? ` last_seen=${selectedAgent.last_seen_at}` : ''}`
1669
1717
  : 'No live agent roster returned yet. Check API key scope or wait for agents to log activity.';
1670
- } else if (state.cursor === 5) {
1718
+ } else if (state.cursor === 2) {
1719
+ const receipts = state.snapshot.arbitrations.receipts;
1720
+ state.lastResult = receipts.length
1721
+ ? receipts.map((receipt, index) => `${index + 1}. ${receipt.id}: ${receipt.resolution}${receipt.decision_id ? `; decision=${receipt.decision_id}` : ''}${receipt.selected_proposal_id ? `; selected=${receipt.selected_proposal_id}` : ''}; ${receipt.exact_next_action || receipt.conflict_type}${receipt.owner_approval_required ? ' Owner approval must be issued from an authenticated Marrow dashboard session.' : ''}`).join(' ')
1722
+ : 'No agent disagreements or arbitration receipts returned yet.';
1723
+ } else if (state.cursor === 6) {
1671
1724
  state.lastResult = state.snapshot.recent_decisions.length
1672
1725
  ? state.snapshot.recent_decisions.map((decision, index) => `${index + 1}. ${decision}`).join(' ')
1673
1726
  : 'No recent decisions returned yet.';
1674
- } else if (state.cursor === 6) {
1727
+ } else if (state.cursor === 7) {
1675
1728
  state.lastResult = state.snapshot.degraded_hooks.length
1676
1729
  ? `Degraded hooks: ${state.snapshot.degraded_hooks.join(', ')}. Fix: ${state.snapshot.fix_commands[0]}`
1677
1730
  : 'No degraded hooks reported.';
1678
- } else if (state.cursor === 7) {
1731
+ } else if (state.cursor === 8) {
1679
1732
  state.lastResult = `Release gates: deploy=${state.snapshot.gates.deploy}, publish=${state.snapshot.gates.publish}, merge=${state.snapshot.gates.merge}.`;
1680
1733
  } else {
1681
1734
  state.lastResult = fleetPanel(state.snapshot).replace(/\n/g, ' ');